C语言入门基础知识(万字笔记)

简介: 本篇文章,将会主要介绍c语言的基本数据类型、基本运算符、语句,三大结构、数组、指针、宏定义等内容

一、前言部分

本篇文章,将会主要介绍c语言的基本数据类型、基本运算符、语句,三大结构、数组、指针、宏定义等内容


二、具体部分

1、基本数据类型

1、基本数据类型

在C语言中,承载一系列信息的数字或中字符都属于数据类型,计算机不能直接识别不同的类型,所以在使用变量时需要在声明语句中指定其数据类型。C语言的数据类型,通俗的理解就是将不同的物品放在不同的盒子中,比如小数存放在浮点型、整数存放在整型、字符存放在字符型等等,在编写C语言时需要根据存放数据的不同,定义不同的类型变量。

c语言有3种基本数据类型:

整型,表示一个整数,通常包括“short”、“int”、“long”等。

实型,“实型”即为浮点型数据,包括“float”、“double”等。

“实型”通常用来表示实数,还可以用来表示整型不能表示的小数。

字符型,“字符型”即为“char”型数据,通常用来表示各种字符。

“字符型”与“ASCII”码一一对应。

其中,short、int、long、char、float、double 这六个关键字代表C 语言里的六种基本数据类型。

在不同的系统上,这些类型占据的字节长度是不同的:

在32 位的系统上

short 占据的内存大小是2 个byte;

int占据的内存大小是4 个byte;

long占据的内存大小是4 个byte;

float占据的内存大小是4 个byte;

double占据的内存大小是8 个byte;

char占据的内存大小是1 个byte。

具体可以用sizeof测试一下即可。


2、运算符、表达式和基本语法语句、三个结构

C语言运算符是说明特定操作的符号 ,它是构造C语言表达式的工具 [2] 。C语言的运算异常丰富,除了控制语句和输入输出以外的几乎所有的基本操作都为运算符处理。除了常见的三大类,算术运算符、关系运算符与逻辑运算符之外,还有一些用于完成特殊任务的运算符,比如位运算符。

一、赋值运算符和赋值表达式

1、赋值运算符

C语言的赋值运算符为等号,表示形式“=”。

此外,还有复合赋值运算符,后续陆续介绍。

2、赋值表达式

“=”的左侧是变量,右侧是常量、变量、表达式、函数等,“=”的含义是将右边的值赋给左侧的变量,程序运行时先计算右侧值,然后赋给左侧变量。

二、算术运算符和算术表达式

对计算机中数据进行算术运算的运算符,称为算术运算符,包括数学中学到的加减乘除和一些扩展。

1、加法和减法运算符

加法运算符为“+”,使运算符两侧的值相加,两侧的值可以是变量、常量和表达式等。

减法运算符为“-”,使运算符左侧的值减去右侧的值。

2、乘法和除法运算符

乘法运算符为“”,使运算符两侧的值相乘。

除法运算符为“/”,使运算符两侧的值相除,”/”左侧的值是被除数,右侧的值是除数。

3、求模运算符

求模运算符为“%”,求出左侧整数除以右侧整数的余数。

上面运算符为二元运算符,所谓二元运算符为运算符两边有两个操作数。

4、符号运算符

“+”(正号)不改变操作数的值及符号, “-“(负号)可用于得到一个数的相反数。

5、自增和自减运算符

自增运算符为“++”,自减运算符为“–”。

自增运算符使运算对象递增1,有两种形式:运算符在变量的左侧,称前缀模式,运算符在变量的右侧,称后缀模式。

前缀形式指变量的值加1作为表达式的值,同时变量的值加1;后缀形式指将变量的值作为表达式的值,然后变量值加1。

符号运算符、自增和自减运算符为一元运算符。

6、复合赋值运算符

复合赋值运算符有:+=、-=、=、/=、%=,分别等同于以下:

x+=y+1等同x=x+(y+1)依次类推,注意:右侧表达式为一个整体。

7、括号()

与数学上的括号一样,能改变运算的顺序。

8、算术表达式

使用算术运算符将运算对象连接起来、符合C语言语法规则的式子。

三、关系运算符和关系表达式

程序设计中需要经常对运算对象之间的大小进行比较,如:大小、相等等关系,这样的运算符称为关系运算符,用关系运算符将数值或表达式连接起来的式子就是关系表达式,满足关系表达式运算符关系的结果称为“真”,否则为假。

常用的关系运算符有:

0cd82a07d826447ea5c45a877a0e5d96.png

四、逻辑运算符和逻辑表达式

有时多个关系表达式组合起来更有用,这时需要逻辑运算符将关系表达式连接起来,用逻辑运算符连接运算对象组成的表达式就是逻辑表达式。

表一:逻辑运算符

f469259b10d441e7bbc6d7320b62a27e.png

逻辑表达式运算结果:

a&&b 只有a和b都是真时,表达式结果为真,有一个为假,表达式结果为假。

a||b a或b有一个为真,表达式结果为真,a和b都为假,表达式结果为假。

!a a为真时,表达式结果为假,a为假时,表达式结果为真。

五、条件运算符和条件表达式

条件运算符是C语言中唯一的一个三目运算符,它需要三个操作数,条件表达式为:

表达式1?表达式2:表达式3。

? : 称为条件运算符

执行情况:

先计算表达式1的值,若为真,则整个表达式的值为表达式2的值,否则,为表达式3的值。

当有多个条件表达式组成的符合条件表达式时,运算顺序从右向左。

如:a>b?a:c>d?c:d相当于a>b?a:(c>d?c:d)

六、逗号运算符和逗号表达式

逗号运算符是特殊的运算符,将两个表达式连接起来,一般形式:

表达式1,表达式2

执行情况:先求解表达式1,再求解表达式2,最后的结果是表达式2的值。

最后就是c语言的基本语句了

分支结构之简单if语句

C语言中的分支结构语句中的if条件语句。

简单if语句的基本结构如下:

if(表达式){执行代码块;}其语义是:如果表达式的值为真,则执行其后的语句,否则不执行该语句。

注意:if()后面没有分号,直接写{}

分支结构之简单if-else语句

简单的if-else语句的基本结构:

be82b84e3a894e5199c74c7bef492d90.png

语义是: 如果表达式的值为真,则执行代码块1,否则执行代码块2。

注意:

if()后面没有分号,直接写{},else后面也没有分号,直接写{}

分支结构之多重if-else语句

C语言中多重if-else语句,其结构如下:

bebb74aaa6ff4e85bbd6a9073b39b138.png

语义是:依次判断表达式的值,当出现某个值为真时,则执行对应代码块,否则执行代码块n。

注意:当某一条件为真的时候,则不会向下执行该分支结构的其他语句。

分支结构之嵌套if-else语句

C语言中嵌套if-else语句。嵌套if-else语句的意思,就是在if-else语句中,再写if-else语句。其一般形式为:

91474415b82b45d7a386988719f55c4e.png

循环结构之while循环

反复不停的执行某个动作就是江湖人称的循环。

C语言中有三种循环结构,先看一下C语言while循环的结构

a188c764fcc24dbe870108628cb3eef7.png

其中表达式表示循环条件,执行代码块为循环体。

while语句的语义是:计算表达式的值,当值为真(非0)时, 执行循环体代码块。

while语句中的表达式一般是关系表达或逻辑表达式,当表达式的值为假时不执行循环体,反之则循环体一直执行。一定要记着在循环体中改变循环变量的值,否则会出现死循环(无休止的执行)。循环体如果包括有一个以上的语句,则必须用{}括起来,组成复合语句。循环结构之do-while循环

C语言中的do-while循环,一般形式如下:

e424b5ea54964c5ab163da93652ef6d1.png

do-while循环语句的语义是:

它先执行循环中的执行代码块,然后再判断while中表达式是否为真,如果为真则继续循环;如果为假,则终止循环。因此,do-while循环至少要执行一次循环语句。

注意:mtianyan: 使用do-while结构语句时,while括号后必须有分号。

循环结构之for循环(一)

c语言中for循环一般形式:

0a82604c728348fdab5a57da1695e70c.png

它的执行过程如下:

执行表达式1,对循环变量做初始化;判断表达式2,若其值为真(非0),则执行for循环体中执行代码块,然后向下执行;若其值为假(0),则结束循环;执行表达式3,(i++)等对于循环变量进行操作的语句;执行for循环中执行代码块后执行第二步;第一步初始化只会执行一次。循环结束,程序继续向下执行。注意:for循环中的两个分号一定要写

循环结构之for循环(二)

在for循环中:

表达式1是一个或多个赋值语句,它用来控制变量的初始值;表达式2是一个关系表达式,它决定什么时候退出循环;表达式3是循环变量的步进值,定义控制循环变量每循环一次后按什么方式变化。这三部分之间用分号 ; 分开。使用for语句应该注意:

for循环中的“表达式1、2、3”均可不写为空,但两个分号(;;)不能缺省。省略“表达式1(循环变量赋初值)”,表示不对循环变量赋初始值。省略“表达式2(循环条件)”,不做其它处理,循环一直执行(死循环)。省略“表达式3(循环变量增减量)”,不做其他处理,循环一直执行(死循环)。表达式1可以是设置循环变量的初值的赋值表达式,也可以是其他表达式。表达式1和表达式3可以是一个简单表达式也可以是多个表达式以逗号分割。

ccd16e5d61844f7e997e43c9b25a9d37.png

表达式2一般是关系表达式或逻辑表达式,但也可是数值表达式或字符表达式,只要其值非零,就执行循环体。各表达式中的变量一定要在for循环之前定义。怎么获得一个数的百位,十位和个位

百位数:num/100 可以获得,因为 int 是整数型,小数部分会省略。比如 765/100 的结果是7十位数:num%100/10 。比如765%100先得到65,65/10得到6个位数:num%10。765%10得到5循环结构之三种循环比较

while, do-while和for三种循环在具体的使用场合上是有区别的,如下:

在知道循环次数的情况下更适合使用for循环;

在不知道循环次数的情况下适合使用while或者do-while循环:如果有可能一次都不循环应考虑使用while循环如果至少循环一次应考虑使用do-while循环。但是从本质上讲,while,do-while和for循环之间是可以相互转换的。

循环结构之多重循环

多重循环就是在循环结构的循环体中又出现循环结构。

在实际开发中一般最多用到三层重循环。

因为循环层数越多,运行时间越长,程序越复杂,所以一般用2-3层多重循环就可以了。另外不同循环之间也是可以嵌套的。

多重循环在执行的过程中,外层循环为父循环,内层循环为子循环,

父循环一次,子循环需要全部执行完,直到跳出循环。父循环再进入下一次,子循环继续执行…

结束语句之break语句

那么循环5次的时候,需要中断不继续训练。在C语言中,可以使用break语句进行该操作.

使用break语句时注意以下几点:

在没有循环结构的情况下,break不能用在单独的if-else语句中。

在多层循环中,一个break语句只跳出当前循环。结束语句之continue语句

那么循环5次的时候,需要中断后继续训练。在C语言中,可以使用continue语句进行该操作

continue语句的作用是结束本次循环开始执行下一次循环。

break语句与continue语句的区别是:

break是跳出当前整个循环,continue是结束本次循环开始下一次循环。

分支结构之switch语句

switch语句结构如下:**

7fb9b826a32f4036abe7e8771470959d.png

switch语句时还应注意以下几点:

在case后的各常量表达式的值不能相同,否则会出现错误。在case子句后如果没有break;会一直往后执行一直到遇到break;才会跳出switch语句。switch后面的表达式语句只能是整型或者字符类型。在case后,允许有多个语句,可以不用{}括起来。各case和default子句的先后顺序可以变动,而不会影响程序执行结果。default子句可以省略不用。

臭名远扬之goto语句

C语言中也有这样的语句,就是goto语句,goto语句是一种无条件分支语句.

goto 语句的使用格式为:

goto 语句标号;**

d43e88587b484fa0b8e2cfced371303e.png

3、函数

C语言提供了大量的库函数: 比如stdio.h提供输出函数

注意:

[] 包含的内容可以省略,数据类型说明省略,默认是 int 类型函数; 参数省略表示该函数是无参函数,参数不省略表示该函数是有参函数;函数名称遵循标识符命名规范;mtianyan: 自定义函数尽量放在 main 函数之前,如果要放在main函数后面的话, 需要在main函数之前先声明自定义函数,声明格式为:[数据类型说明] 函数名称([参数]);函数调用

我们需要用到自定义的函数的时候,就得调用它,那么在调用的时候就称之为函数调用。

在C语言中,函数调用的一般形式为:

函数名([参数]);注意:

对无参函数调用的时候可以将[]包含的省略。

[]中可以是常数,变量或其它构造类型数据及表达式,多个参数之间用逗号分隔。有参与无参

在函数中不需要函数参数的称之为无参函数,在函数中需要函数参数的称之为有参函数。

有参和无参函数的一般形式如下:

cd3760640e664849a6066398cb1579d3.png

有参函数和无参函数的唯一区别在于:函数 () 中多了一个参数列表。

有参函数更为灵活,输出的内容可以随着n的改变而随意变动,只要在main函数中传递一个参数就可以了而在无参函数中输出的相对就比较固定,当需要改动的时候还需要到自定义的方法内改变循环变量的值。mtianyan: 形参与实参

函数的参数分为形参和实参两种。

形参是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。就类似小明,说了的话而不实际行动;

实参是在调用时传递该函数的参数。就如小刚能实际行动起来。

函数的形参和实参具有以下特点:

形参只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。

实参可以是常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值等办法使实参获得确定值。

在参数传递时,实参和形参在数量上,类型上,顺序上应严格一致,否则会发生类型不匹配的错误。

e793dd35e74a43c6aab570019e79be14.png

**函数的返回值

函数的返回值是指函数被调用之后,执行函数体中的程序段所取得的并返回给主调函数的值。

函数的返回值要注意以下几点:

函数的值只能通过return语句返回主调函数。return语句的一般形式为:

return 表达式 或者为:return (表达式);函数值的类型和函数定义中函数的类型应保持一致。notes: 如果两者不一致,则以函数返回类型为准,自动进行类型转换。

没有返回值的函数,返回类型为 void。注意:

void 函数中可以有执行代码块,但是不能有返回值.

注意: void函数中如果有return语句,该语句只能起到结束函数运行的功能。其格式为: return;

内部函数与外部函数

在C语言中不能被其他源文件调用的函数称谓内部函数 ,内部函数由static关键字来定义,因此又被称谓静态函数,形式为:static [数据类型] 函数名([参数])这里的static是对函数的作用范围的一个限定,限定该函数只能在其所处的源文件中使用,因此在不同文件中出现相同的函数名称的内部函数是没有问题的。在C语言中能被其他源文件调用的函数称谓外部函数 ,外部函数由extern关键字来定义,形式为:extern [数据类型] 函数名([参数])C语言规定,在没有指定函数的作用范围时,系统会默认认为是外部函数,因此当需要定义外部函数时extern也可以省略。

4、指针与数组

4、指针与数组

1、数组:储存数据类型相同的一系列元素

例如 int a[100]; 在这里 a 数组储存100个 int 型元素,在这里 [] 这个符号就是告诉计算机 a 是一个数组。

值得一提的是数组的下标访问数组的,数组中的 a[0] 一般表示你初始化的第一个值。

2、初始化数组

前面介绍过了,数组可以储存数据类型相同的一系列元素,所以初始化数组必不可少的一步就是告诉计算机这个数组储存的数据类型!**


int a[10]={1,33,0,919,3,199} //int型a数组初始化值为1,33,0,919,3,199

数组 状态 出现情况

int a[2] int a[2]={1,2} 元素与 [ ]内 数字相等 a[0]=1 a[1]=2

int a[2] int a[2]={1} 元素与小于[ ]内数字 a[0]=1 a[1]=0

int a[2] int a[2]={1,23,55} 元素与大于[ ]内数字 系统报错

还有一种情况 int a[] , [] 内为空白这样就没有告诉计算机元素的个数,但是计算机会根据初始化列表确定数组的大小,这样就可以有效防止初始化值的个数超过数组的大小。

3、数组元素赋值

#include<stdio.h>
int main(void)
{
  int a[10];
  for(int i=0;i<10;i++)
  {
    a[i]=i;
    printf("%2d\n",a[i]);
  }
  return 0;
}

这里有几点值得注意:

这行代码中 a[i]=i,规定数组 [ ] 中不能有变量(未初始化),但这里用了 i 却可以,是因为在这之前 i 已经被初始化一个值。

printf(“%2d\n”,a[i]); 这行代码中有很多同学会写成 &a[i] ,往往输出会得到一串奇怪的数字,这是为什么?我们先仔细阅读下面这篇文字:

数组名代表数组首元素的地址

数组的地址需要取地址符&才能看到

数组首元素的地址值与数组的地址值相同

下面就是令无数uu头疼的指针部分了!!!

如果问 C 语言中最重要、威力最大的概念是什么,答案必将是指针!

威力大,意味着使用方便、高效,同时也意味着语法复杂、容易出错。指针用的好,可以极大的提高代码执行效率、节约系统资源;如果用的不好,程序中将会充满陷阱、漏洞。

首先我们先记住一个概念:指针也是一个变量,只是这个变量的值比较特殊,它存放的是地址;

e0c30cd0554140eba60f42c50903feb5.png

**指针和内存

我们结合一个例子来讲指针:int *p;

在这里我们定义了一个指针p,但是现在没没有对它进行初始化,在C语言中编译过程中这条语句会给p开辟一块内存空间,空间大小呢由类型决定在32位系统上int 为四个字节,所以现在就为p变量开辟了大小为4个字节的空间,并且限定了这4个字节里面只能存放某个内存的地址(什么意思?就是说申请的空间只能存放地址,而不是具体数值),即使你存入别的任何数据都将会被当作地址处理,而且申请的这个内存地址开始连续的4个字节上只能存某个int类型的数据。

int *p //p只能存放int类型的地址

char p //p只能存放cahr 类型的地址

float p //p只能存放float类型的地址

也就是说你声明一个指针的时候:申请的类型是什么类型他就是什么类型的指针。

指针的使用

我们来看一下面的代码:


int a=10;
int *p;
p=&a;
printf("a=%d p=%d",a,*p);

**&符号为取地址符:&a就是取a的地址;

这里我们把a的地址赋给指针变量p,我们可以理解现在指针变量p里面存放的是a的地址(p指针指向a的地址)那我们现在我们就有两种办法可以得到我们的数据10;

一个是变量名:a 另外一个就是指针p,星号p(p)的意思是把指针p里面的值取出来,那么我们都知道现在p里面存放的是a的地址程序取值的时候直接根据地址去内存里面把值取出来。

总结一下:

1、指针也是变量。

2、指针里面放的是地址;(你把哪个变量的地址给他他就存谁的地址)。

3、使用指针时要和声明的类型一致。

在许多 C 程序中,指针常被用于引用数组,或者作为数组的元素。指向数组的指针常被简称为数组指针(array pointer),而具有指针类型元素的数组则被称为指针数组(pointer array)。

数组指针

为了便于举例,下面的描述均以一个 int 数组为例。同样的原理可以应用于其他类型数组,包括多维数组。

要声明指向数组类型的指针,必须使用括号,如下所示:

int ( arrPtr)[10] = NULL; // 一个指针,它指向一个有10个int元素的数组

如果没有括号,则声明 int*arrPtr[l0];表示 arrPtr 是一个具有 10 个 int 类型指针的数组。

在该例中,指向有 10 个 int 元素的数组的指针会被初始化为 NULL。然而,如果把合适数组的地址分配给它,那么表达式 *arrPtr 会获得数组,并且(*arrPtr)[i] 会获得索引值为 i 的数组元素。根据下标运算符的规则,表达式(*arrPtr)[i] 等同于 *((*arrPtr)+i)。因此,arrPtr 获得数组的第一个元素,其索引值为 0。

为了展示数组指针 arrPtr 的几个运算,下例使用它来定位一个二维数组的某些元素,也就是矩阵内的某些行:

int matrix[3][10]; // 3行,10列的数组
// 数组名称是一个指向第一个元素的指针,也就是第一行的指针
arrPtr = matrix; // 使得arrPtr指向矩阵的第一行
(*arrPtr)[0] = 5; // 将5赋值给第一行的第一个元素
arrPtr[2][9] = 6; // 将6赋值给最后一行的最后一个元素
++arrPtr; // 将指针移动到下一行
(*arrPtr)[0] = 7; // 将7赋值给第二行的第一个元素

在初始化赋值后,arrPtr 指向矩阵的第一个行,正如矩阵名称 matrix 一样。在这种情况下,使用 arrPtr 获取元素的方式与使用 matrix 完全一样。例如,赋值运算(arrPtr)[0]=5 等效于 arrPtr[0][0]=5 和 matrix[0][0]=5。

然而,与数组名称 matrix 不同的是,指针名称 arrPtr 并不代表一个常量地址,如运算 ++arrPtr 所示,它进行了自增运算。这个自增运算会造成存储在数组指针的地址增加一个数组空间大小,在本例中,即增加矩阵一行的空间大小,也就是 10 乘以 int 元素在内存中所占字节数量。

如果想把一个多维数组传入函数,则必须声明对应的函数参数为数组指针。最后要注意的是,如果 a 是一个具有 10 个 int 类型元素的数组,那么无法使用下面的方式对前面例子中的指针 arrPtr 赋值:

arrPtr = a; // 错误:指针类型不匹配

错误的原因是,数组名字,例如上文的 a,会被隐式地转换为指针,指向数组第一个元素,而不是指向整个数组。指向 int 的指针没有被隐式地转换为指向 int 数组的指针。本例中的赋值操作需要显式的类型转换,在类型转换运算符中明确指定目标类型是

int () [10]:

arrPtr = (int (*)[10])a; // 合法

在前文 arrPtr 的声明语句(int(arrPtr)[10]=NULL;)中,删除其中标识符 arrPtr,就可得到 int()[10],即对应的数组指针类型。然而,为了提高可读性和灵活性,可以利用 typedef 为所用的类型定义一个简单的名字:


typedef int ARRAY_t[10]; // 定义一个“具有10个元素数组”类型名称
ARRAY_t a, // 具有该类型的数组
*arrPtr; // 一个指向该数组类型的指针
arrPtr = (ARRAY_t *)a; // 使得arrPtr指向a

指针数组

指针数组(也就是元素为指针类型的数组)常常作为二维数组的一种便捷替代方式。一般情况下,这种数组中的指针会指向动态分配的内存区域。

例如,如果需要处理字符串,可以将它们存储在一个二维数组中,该数组行空间大小必须足以存储下可能出现的最长字符串:


#define ARRAY_LEN 100
#define STRLEN_MAX 256
char myStrings[ARRAY_LEN][STRLEN_MAX] =
{“会出错的事,总会出错”
“世上没有绝对正确的事情”
“每个解决办法都会衍生出新的问题”
};

然而,这个方式造成内存浪费,25600 字节中只有一小部分被实际使用到。一方面,短字符串会让大部分的行是空的;另一个方面,有些行根本没有用到,但却得为它预留内存。

一个简单的解决方案是,使用指针数组,让指针指向对象(在此处的对象就是字符串),然后只给实际存在的对象分配内存(未用到的数组元素则是空指针)。


#define ARRAY_LEN 100
char *myStrPtr[ARRAY_LEN] = // char指针的数组
{“会出错的事,总会出错。”
“世上没有绝对正确的事情。”
“每个解决办法都会衍生出新的问题。”
};

对象在内存中的存储情况:

28976f2f2e284e9c9da886d16e5a5bad.png

**尚未使用的指针可以在运行时指向另一个字符串。所需的存储空间可以利用这种常见方法来动态地保留。当不再需要该内存时,可以释放。


5、结构体、联合体

5、结构体、联合体

1,结构体即为多个基本数据类型组合而成的数据类型。结构体本质上同int等一样同为数据类型,可以定义变量,内部成员不能直接赋值。**


struct Man
{
int age;
int score;
};

2,结构体常同typedef类型重命名一同使用,如下:

typedef struct Man
{
int age; //这样就好了
int score;
}MAN;
int main()
{
MAN man1 = {,};
}

3,结构体内存对齐


typedef struct Man
{
char age; //这样就好了
int score;
}MAN;
int main()
{
printf("%d",sizeof(MAN));
}

以上代码输出的值为8,而不是直观意义上的5,这是因为结构体中的成员是对齐的,这里按4字节对齐。当CPU访问正确对齐的数据时,它的运行效率最高,数据对齐不是内存结构的一部分,而是CPU结构的一部分。

 结构体成员对齐是按照类型大小对齐的,而结构体按照长度最大的类型对齐。如成员中char按1字节对齐,short按2字节对齐,int按照4字节对齐等,这里的结构体成员最长的是int为4字节,所以这个结构体按照4字节对齐。

联合体

 1,联合体本质上为不同数据类型共享存储空间,此空间要大到足够容纳最"宽"的成员。它的所有成员相对于基地址的偏移量都为0。


union U
{
char s[];
int n;
double d;
};

2,联合体通常与结构体共用,通过不同方式给变量赋值


union REG
{
struct
{
unsigned short a;
unsigned short b;
} H;
  unsigned int R;
} Reg;

宏定义

六、宏定义

在 C 语言中,可以采用命令 #define 来定义宏。该命令允许把一个名称指定成任何所需的文本,例如一个常量值或者一条语句。在定义了宏之后,无论宏名称出现在源代码的何处,预处理器都会把它用定义时指定的文本替换掉。

关于宏的一个常见应用就是,用它定义数值常量的名称:


#define ARRAY_SIZE 100
double data[ARRAY_SIZE];

这两行代码为值 100 定义了一个宏名称 ARRAY_SIZE,并且在数组 data 的定义中使用了该宏。惯例将宏名称每个字母采用大写,这有助于区分宏与一般的变量。上述简单的示例也展示了宏是怎样让 C 程序更有弹性的。

通常情况下,程序中往往多次用到数组(例如上述 data)的长度,例如,采用数组元素来控制 for 循环遍历次数。当每次用到数组长度时,用宏名称而不要直接用数字,如果程序的维护者需要修改数组长度,只需要修改宏的定义即可,即 #define 命令,而不需要修改程序中每次用到每个数组长度的地方。

在翻译的第三个步骤中,预处理器会分析源文件,把它们转换为预处理器记号和空白符。如果遇到的记号是宏名称,预处理器就会展开(expand)该宏;也就是说,会用定义的文本来取代宏名称。出现在字符串字面量中的宏名称不会被展开,因为整个字符串字面量算作一个预处理器记号。

无法通过宏展开的方式创建预处理器命令。即使宏的展开结果会生成形式上有效的命令,但预处理器不会执行它。

在宏定义时,可以有参数,也可以没有参数。

本章终!


相关文章
|
1月前
|
安全 编译器 C语言
C++入门1——从C语言到C++的过渡
C++入门1——从C语言到C++的过渡
48 2
|
1月前
|
存储 Java 编译器
初识C语言1——C语言入门介绍
初识C语言1——C语言入门介绍
27 1
|
28天前
|
C语言
回溯入门题,数据所有排列方式(c语言)
回溯入门题,数据所有排列方式(c语言)
|
3月前
|
C语言
C语言------程设设计入门
这篇文章是C语言程序设计的入门教程,涵盖了C程序的实现过程、VC集成开发环境的使用、基本数据类型的使用、格式控制字符的作用,以及通过示例代码演示了如何使用printf()函数输出不同类型的数据。
C语言------程设设计入门
|
4月前
|
存储 Java C语言
【C语言入门】初识C语言:掌握编程的基石
【C语言入门】初识C语言:掌握编程的基石
66 4
【C语言入门】初识C语言:掌握编程的基石
|
3月前
|
NoSQL Java 编译器
C语言从入门到精通该怎样学?
持续学习与实践:编程是一门需要不断学习和实践的技能,要保持对新技术和新知识的敏感性,并持续进行编程实践。
54 1
|
3月前
|
测试技术 C语言 C++
【C语言刷题训练——6】鹏哥C语言刷题训练营笔记,含代码讲解改进
【C语言刷题训练——6】鹏哥C语言刷题训练营笔记,含代码讲解改进
|
3月前
|
存储 C语言
【C语言】鹏哥C语言刷题训练营——第5节内容笔记(含代码全面分析和改进,讲解)
【C语言】鹏哥C语言刷题训练营——第5节内容笔记(含代码全面分析和改进,讲解)
|
4月前
|
存储 Java 程序员
【C语言入门】C语言入门:探索编程世界的基础概念
【C语言入门】C语言入门:探索编程世界的基础概念
103 2
|
4月前
|
前端开发 C语言 C++
C语言入门02---环境搭建
C语言入门02---环境搭建