C语言笔记(二)

注释


编译器会用空格代替代码中原来的注释,并先于预处理指令执行
/*…*/ 这种形式的注释不能嵌套
只要斜杠(/)和星号(*)之间没有空格,都会被当作注释的开始。例如这样:
y = x/*p;

\ 是一个接续符,表示断行。编译器会将反斜杠剔除掉,跟在反斜杠后面的字符自动接续到前一行。但是注意:反斜杠之后不能有空格,反斜杠的下一行之前也不能有空格。(有的编译器有空格可以通过)。\ 还能被用作转义字符的开始标识。

 

位运算符


交换两个变量的值:a ^= b; b ^= a; a ^= b;

0x01<<2+3;
// ==32
// "+"号的优先级比移位运算符的优先级高

在 32 位系统下:

0x01<<2+30;
0x01<<2-3;

左移和右移的位数不能大于数据的长度,不能小于0。(有的编译器还是可以通过的)

对于有符号数,在>>右移时,符号位将随同移动。当为正数时,最高位补0;

而为负数时,符号位为 1,最高位是补 0 或是补 1 取决于编译系统的规定。Turbo C 和很多系统规定为补 1。

 

常用的位操作宏:

#define  SETBIT(x, y)  ((x) |=  (y))
#define  CLRBIT(x, y)  ((x) &= ~(y))
#define TOGLBIT(x, y)  ((x) ^=  (y))
#define TESTBIT(x, y)  ((x) &   (y))
#define SETBIT(x, y)  ((x) |= 1<<(y))
#define CLRBIT(x, y)  ((x) &= ~(1<<(y)))

 

花括号


char a[10]{ = “abcde”};

花括号的作用就是打包。

 

自加减++、--


i=3;
(++i)+(++i)+(++i);  //未定义行为,取决于编译器
// TCC:15,    VC6:16,    GCC:18
i=3;
(i++)+(i++)+(i++);

自加或自减运算是在本计算单位计算结束( 遇到 , 或 ; )之后再自加或自减。

int i,j;
i=0;
j=(i++,i++,i++);    //j=2,i=3

i=0;
j=(++i,++i,++i);    //j=3

 

/ 和 %


有2组计算

3/2; (-3)/2; 3/(-2); (-3)/(-2);
3%2; (-3)%2; 3%(-2); (-3)%(-2);

 

优先级


. []和() 的优先级比 * 高
== 和 != 优先级高于位操作
== 和 != 优先级高于赋值符
算术运算符高于位移运算符

 

*p++

* 和 ++ 是同优先级,其结合方向为自右向左,因此 *p++ 等价于

*(p++)

 

预处理


预处理指令是编译器的功能,不是C语言的一部分。

宏定义

ANSI 标准 C 还定义了如下几个宏:
_LINE_ 表示正在编译的文件的行号
_FILE_ 表示正在编译的文件的名字
_DATE_ 表示编译时刻的日期字符串,例如: "25 Dec 2007"
_TIME_ 表示编译时刻的时间字符串,例如: "12:30:55"
_STDC_ 判断该文件是不是定义成标准 C 程序

下面的写法都是对的:

#define EMPTY
# define EMPTY

在使用宏函数的时候,宏名和参数之间的空格会被编译器忽略掉。定义宏的时候空格是有效的。

 

#error 预处理指令的作用是,编译程序时,只要遇到 #error 就会生成一个编译错误提示消息,并停止编译。其语法格式为:
#error error-message

 

#line 的作用是改变当前行数和文件名称,它们是在编译程序中预先定义的标识符。命令的基本形式如下:
#line number["filename"]

 

#pragma 指令的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。

 

#和##都是预处理命令。

 

断言assert

#if !defined(NDBUG)
#define assert(p)    if(!(p)){fprintf(stderr,\
        "Assertion failed: %s, file %s, line %d\n",\
        #p, __FILE__, __LINE__);abort();}
#else
#define assert(p)
#endif

 

main函数


无参的写法

int main(void)

有参的写法

int main(int argc, char *argv[])
int main(int argc, char **argv)

argc -- 命令行参数总个数,包括 可执行程序名

argv[0] -- 可执行程序名

argv[i] -- 第 i 个参数

例子

# ./a.out Love Live !

该例子中 argc 为4,argv[0]=a.out, argv[1]=Love, argv[2]=Live, argv[3]=!

 

main函数的位置

C语言规定,main函数是程序的入口。所以不管把main函数放在源程序的任意地方,都是没有错的。

 

数组 int a[5];


定义一个数组 a 时,编译器根据指定的元素个数和元素的类型分配确定大小的一块内存,并把这块内存的名字命名为 a
名字 a 一旦与这块内存匹配就不能被改变。a[0],a[1]等为 a 的元素,但并非元素的名字。数组的每一个元素都是没有名字的。

&a[0]和&a 的区别(I)

a[0]是一个元素,a 是整个数组,虽然 &a[0] 和 &a 的值一样,但其意义不一样。

前者是数组首元素的首地址,而后者是数组的首地址

 

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

(1) a 作为右值时其意义与 &a[0] 是一样,代表的是 数组首元素的首地址,而不是数组的首地址。

int *p; p = &a[0]; 或者 int *p; p = a; 两者同意。

(2) a 不能作为左值!只能访问数组的某个元素而无法把数组当一个总体进行访问。

 

&a[0] 和 &a 的区别(II)

int main()
{
    char a[5] = {'A','B','C','D'};
    char (*p3)[5] = &a;
    char (*p4)[5] = a;
    return 0;
}

p3 和 p4 都是数组指针,指向的是整个数组。p3 这个定义的“=”号两边的数据类型完全一致,而 p4 这个定义的“=”号两边的数据类型就不一致了。左边的类型是指向整个数组的指针,右边的数据类型是指向单个字符的指针。但由于 &a 和 a 的值一样,而变量作为右值时编译器只是取变量的值,所以运行并没有什么问题。

p3+1 和 p4+1 的值又是多少呢?

 

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

 

指针


两个指针指向同一个变量

int *p1,*p2;
int x = 11;
p1 = &x;
p2 = p1;

 

野指针产生的原因

定义指针变量时未初始化;指针释放后之后未置空。free和delete只是把指针所指的内存给释放掉,但并没有把指针本身干掉。此时指针指向的就是“垃圾”内存。释放后的指针应立即将指针置为NULL,防止产生“野指针”。

 

动态内存的使用

1.使用malloc函数申请内存空间后,要检查指针p是否为NULL

char *p = (char *)malloc(sizeof(char)*LENGTH+1);

if(NULL == p){ }

2.及时为指针p赋初值,防止未初始化内存就去使用

char *p = NULL;

3.申请与释放内存必须配对(malloc-free)

2. 用完指针之后也将指针变量的值设置为 NULL,防止产生“野指针”
free(p);
p = NULL;

 

posted @ 2016-03-27 17:56  luoxu34  阅读(508)  评论(0编辑  收藏  举报