当前位置:编程学习 > C/C++ >>

C语言要点

一关键字:

1.       static:

第一个作用:修饰变量。变量又分为局部和全局变量,但它们都存在内存的静态区。由于被 static修饰的变量总是存在内存的静态区,所以即使这个函数运行结束,这个静态变量的值还是不会被销毁,函数下次使用时仍然能用到这个值。

static int j;

void fun1(void)

{

static int i = 0;

i++;

}

void fun2(void)

{

j=0;

j++;

}

int main()

{

for(k=0; k<10; k++)

{

fun1();

fun2();

}

return 0;

第二个作用:修饰函数。函数前加 static使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。

2 .sizeof

         sizeof是关键字不是函数

3. signed、unsigned关键字

4. float变量与“零值”进行比较

5.if else语句

         先处理正常情况,再处理异常情况。

6.循环语句风格

         在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放

在最外层,以减少 CPU 跨切循环层的次数。

7. void

         void不能代表一个真实的变量

         void a; //错误

function(void a); //错误

8.const

         const修饰的只读变量必须在定义的同时初始化,编译器通常不为普通 const只读变量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的值,没有了存储与读内存的操作,使得它的效率也很高。

         a)修饰指针

const int *p; // p 可变,p指向的对象不可变

int const *p; // p可变,p指向的对象不可变

int*const p; //p不可变,p指向的对象可变

const int *const p; //指针 p和 p指向的对象都不可变

b)修饰函数参数

const修饰符也可以修饰函数的参数,当不希望这个参数值被函数体内意外改变时使

用。例如:

void Fun(const int i);

9.extern

         extern就相当于他们的这些区别于中国人的特性。extern可以置于变量或者函数前,以

标示变量或者函数的定义在别的文件中。

       

10.struct

         空结构体的大小就定位 1个byte。struct的成员默认情况下属性是 public的,而class成员却是 private的。

11.union

         union维护足够的空间来置放多个数据成员中的“一种”,而不是为每一个数据成员配置空间,在union中所有的数据成员共用一个空间,同一时间只能储存其中一个数据成员,所有的数据成员具有相同的起始地址。例子如下:

union StateMachine

{

char character;

int number;

char *str;

double exp;

};

一个 union只配置一个足够大的空间以来容纳最大长度的数据成员.

 

12 typedef

       

 

 

二预处理

1.#if

         #if 的一般含义是如果#if后面的常量表达式为true,则编译它与#endif之间的代码,否则跳过这些代码。命令#endif 标识一个#if块的结束。#else命令的功能有点象 C语言中的 else ,#else建立另一选择(在#if 失败的情况下)。#elif命令意义与 else if 相同,它形成一个 if else-if阶梯状语句,可进行多种编译选择。

 

2.#define

         #define会原样替换,要搞定它其实很简单,别吝啬括号就行了。

例子:

定义一个宏函数,求 x 的平方:

#define SQR (x) x * x

试试:假设 x 的值为10,SQR (x)被替换后变成 10*10。没有问题。

再试试:假设 x 的值是个表达式10+1,SQR (x)被替换后变成10+1*10+1。问题

这并不是我想要得到的。怎么办?括号括起来不就完了?

#define SQR (x) ((x)*(x))

       

三指针和数组

         注意!!!:指针的偏移值是起始地址加上指针指向对象的size。

         1.数组名a作为左值和右值的区别

         数组名不能作为左值,对指针进行加 1操作,得到的是下一个元素的地址,而不是原有地址值直接加 1。所以,一个类型为 T的指针的移动,以 sizeof(T) 为移动单位。    main()

{

int a[5]={1,2,3,4,5};

int *ptr=(int *)(&a+1);

printf("%d,%d",*(a+1),*(ptr-1));

}

&a + 1: 取数组 a的首地址,该地址的值加上 sizeof(a) 的值,即 &a + 5*sizeof(int),也

就是下一个数组的首地址,显然当前指针已经越过了数组的界限。(int *)(&a+1):则是把上一步计算出来的地址,强制转换为 int * 类型,赋值给 ptr。*(a+1): a,&a的值是一样的,但意思不一样,a是数组首元素的首地址,也就是 a[0]的首地址,&a是数组的首地址,a+1是数组下一元素的首地址,即a[1]的首地址,&a+1是下一个数组的首地址。所以输出 2*(ptr-1): 因为 ptr 是指向 a[5],并且 ptr是 int * 类型,所以 *(ptr-1)是指向 a[4] ,输出 5。

 

2.数组和指针的关系

文件 1中定义如下:

char a[100];

文件 2 中声明如下:

extern char *a;

这里,文件1 中定义了数组a,文件2中声明它为指针。这有什么问题吗?平时不是总说数组与指针相似,甚至可以通用吗?但是,很不幸,这是错误的。通过上面的分析我们也能明白一些,数组就是数组,指针就是指针,它们是完全不同的两码事!他们之间没有任何关系,只是经常穿着相似的衣服来迷惑你罢了。下面就来分析分析这个问题:

 

定义和声明之间的区别,定义分配的内存,而声明没有。定义只能出现一次,而声明可以出现多次。这里extern告诉编译器 a这个名字已经在别的文件中被定义了,下面的代码使用的名字 a是别的文件定义的。再回顾到前面对于左值和右值的讨论,我们知道如果编译器需要某个地址(可能还需要加上偏移量)来执行某种操作的话,它就可以直接通过开锁动作(使用“*”这把钥匙)来读或者写这个地址上的内存,并不需要先去找到储存这个地址的地方。相反,对于指针而言,必须先去找到储存这个地址的地方,取出这个地址值然后对这个地址进行开锁(使用“*”这把钥匙)。如下图:

这就是为什么 extern char a[]与 extern char a[100]等价的原因。因为这只是声明,不分配空间,所以编译器无需知道这个数组有多少个元素。这两个声明都告诉编译器 a是在别的文件中被定义的一个数组,a 同时代表着数组 a的首元素的首地址,也就是这块内存的起始地址。数组内地任何元素的的地址都只需要知道这个地址就可以计算出来。

但是,当你声明为 extern char *a时,编译器理所当然的认为 a是一个指针变量,在 32位系统下,占 4个byte。这 4个byte里保存了一个地址,这个地址上存的是字符类型数据。虽然在文件 1中,编译器知道a 是一个数组,但是在文件 2中,编译器并不知道这点。大多数编译器是按文件分别编译的,编译器只按照本文件中声明的类型来处理。所以,虽然a实际大小为 100个byte,但是在文件 2中,编译器认为a 只占4个byte。

我们说过,编译器会把存在指针变量中的任何数据当作地址来处理。所以,如果需要

访问这些字符类型数据,我们必须先从指针变量 a中取出其保存的地址。如下图:

 

显然,按照上面的分析,我们把文件 1中定义的数组在文件2 中声明为指针会发生错误。同样的,如果在文件 1中定义为指针,而在文件中声明为数组也会发生错误:

文件 1

char *p = “abcdefg”;

文件 2

extern char p[];

在文件 1 中,编译器分配4个byte空间,并命名为 p。同时p里保存了字符串常量“abcdefg”的首字符的首地址。这个字符串常量本身保存在内存的静态区,其内容不可更改。在文件 2中,编译器认为 p是一个数组,其大小为 4 个byte,数组内保存的是 char类型的数据。在文件 2中使用p 的过程如下图:

 

3. 数组参数与指针参数

无法向函数传递一个数组,C语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。

补充:软件开发 , C语言 ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,