-
结构的存储分配
结构内部包含了大量的未用空间
编译器按照成员列表的顺序一个接一个地给每个成员分配内存,只有当存储成员是需要满足正确的边界对齐要求时,成员之间才有可能出现用于填充的额外内存空间.
若结构体为:struct JGT{ char a; int b; char c; };
如果整型值长度为4个字节,并且它的起始存储位置必须能够被4整除,那么这个结构在内存中存储结构将如下图所示:
显然上图所示的内存利用率不高,想要提高利用率就要在声明中对结构体的成员列表重新排列,让那些对边界要求最严格的成员首先出现,对边界要求最弱的成员最后出现,就可以最大限度地减少因边界对齐而带来的空间损失.
-
printf函数
下面代码会输出什么?int main(int argc, char *argv[]) { int a=10,b=20,c=30; printf("%d %d\n",b=b*c,c=c*2); printf("%d\n",printf("%d",a+b+c)); return 0; }
- printf函数的返回值是所打印字符的长度.
- printf函数运行时 在栈上申请存储空间,规则为先进后出,所以printf()表现为参数从右向左运算.
c=60(c=2),b=1200(b=c).
-
switch语句
(常量表达式:在编译期间进行求值的表达式)
下面程序的运行结果?
int main(int argc, char *argv[]) { int a,b=2,c=5; for (a=1;a<4;a++) { switch(a) { b=99; // 此行代码被跳过 case 2: printf("c is %d\n",c); break; default: printf("a is %d\n",a); case 1: printf("b is %d\n",b); break; } } return 0; }
1.输出结果为:
b is 2
c is 5
a is 3
b is 22.switch语句中case之上的语句(赋值语句/输出语句…)会被跳过.
-
返回值为函数指针的函数
解释下面代码输出结果?size_t q(size_t b) { return b; } size_t (*p(char *str))(size_t a) { printf("%s\n",str); retur q; } int main(int argc,char *argv[]) { char str[]="XiyouLinuxGroup"; printf("%lu\n",p(str)(strlen(str))); return 0; }
- size_t (*p(char *str))(size_t a)是一个返回值为函数指针的函数,其返回值为size_t( *)(size_t a)==>一个指向函数的指针.
该指针指向的函数有size_t类型的参数
int main(int argc, char *argv[])
{
int a[3][4];
printf("%p%p%p%p%p%p\n",&a[0][0],a[0],a,a[0]+1,a+1,a[1]);
return 0;
}
- a作为函数参数,为int型指针 (int *)
- sizeof(a) 返回整个数组的长度 (int [3][4])
- &a 为数组指针,( int(*)[3][4] )
- 在表达式中,它的类型为int (const*)[4],==>指向数组首元素a[0]的常量指针,a[0]=>int[4]
- a=(a+0)=*(0+a)=a[0]=0[a] -------> a=&a[0]
- a+1 ===&a[0]+1(跨过一整行元素)
- a[0]+1===&a[0][1] (跨过一个元素)
- &a+1 (跨过一整个数组)
若a[0][0]的地址为0x00000000,求程序输出结果:
0x00000000
0x00000000
0x00000000
0x00000004
0x00000010
0x00000010
-
字符串有关函数
int strcmp(char const *s1,char const *s2) ;
$- 如果s1小于s2,strcmp函数返回一个小于0的值;如果s1大于s2,strcmp函数返回一个大于0的值;如果两个字符串相等,strcmp函数就返回0.
不能有
if(strcmp(a,b))
此类代码出现,运行结果恰恰与预期结果相反char *strncpy(char *s1,char const *s2,size_t len); char *strncat(char *s1,char const *s2,size_t len); int strncmp(char const *s1,char const *s2,size_t len);
标准库还包含了一些函数,以一种不同的方式处理字符串。这些函数接受一个显示的长度参数,用于限定进行复制或比较的字符串,可以防止难以预料的长字符串从它们的目标数组溢出。
如果原参数和目标参数发生重叠,
strncpy
和strncat
的结果就是未定义的。和
strcpy
一样,strncpy
把源字符串的字符复制到目标数组。 它总是正好向s1
写入len
个字符,如果strlen(s2)
的值小于len
,s1
数组就用额外的NULL
字节填充到len
长度,如果strlen(s2)
的值大于len
,那么只有len
个字符被复制到s1
中。此时,它的结果将不会以NULL
字节结尾
-
内存操作
当遇到非字符串数据内部包含零值的情况,就无法使用字符串函数来处理这种类型的数据(遇到第一个
NULL
字节就会停止工作),但方法总比困难多,这个时候我们就可以使用另外一组操作与字符串函数类似的函数,这些函数能够处理任意的字节序列。原型:
void *memcpy(void *s1,void const *s2,size_t length);
void *memmove(void *s1,void const *s2,size_t length);
void *memcmp(void const *a,void const *b,size_t length);
void *memchr(void const *a,int ch,size_t length);
void *memset(void *a,int ch,size_t length);
它们在遇到NULL字节时不会停止操作memcpy
从s2
的起始位置复制length
个字节到s1
的内存起始位置,并且可以复制任何类型的值。(若s1
和s2
出现重叠,则函数的结果未定义)
memmove
函数执行相同的功能,但它能够正确处理源参数和目标参数出现重叠的情况。
memcmp
函数比较两个序列的字节。
memchr
函数在一个字节序列中查找一个特定的值。
memset
函数把一序列字节初始化为一个特定的值。
-
#define替换
- 在程序中扩展#define定义符号和宏时,需要涉及几个步骤
- 在调用宏时,首先对参数进行检查,看看是否包含了任何由#define定义的符号。如果是,它们首先被替换。
- 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被它们的值所代替。
- 最后,再次对结果文本进行扫描,看看它是否包含了任何由#define定义的符号。如果是,就重复上述过程。
在用#define定义时斜杠(“\”)是用来续行的
- “#”用来把参数转换成字符串,是给参数加上双引号
- "##"则用来连接前后两个参数,把它们变成一个参数
- “#@”是给参数加上单引号
-
static关键字
-
全局变量
全局变量定义在函数体外部,在全局数据区分配存储空间,且编译器会自动对其初始化。普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量)。
静态全局变量仅对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。
在定义不需要与其他文件共享的全局变量时,加上static关键字能够有效地降低程序模块之间的耦合,避免不同文件同名变量的冲突,且不会误使用。
-
局部变量
在任何一个函数内部定义的变量(不加static修饰符)都属于局部变量。编译器一般不对普通局部变量进行初始化,所以 它的值在初始化时是不确定的,除非对其赋值。
普通局部变量存储于进程栈空间,使用完毕会立即释放
静态局部变量使用static修饰符定义,即使在声明时未赋初值,编译器也会把它初始化为0。且静态局部变量存储于进程的全局数据区,即使函数返回,它的值也会保持不变。
变量在全局数据区分配内存空间;编译器自动对其初始化
其作用域为局部作用域,当定义它的函数结束时,其作用域随之结束 -
函数
函数的使用方式与全局变量类似,在函数的返回类型前加上static,就是静态函数。
- 静态函数只能在声明它的文件中可见,其他文件不能引用该函数
- 不同的文件可以使用相同名字的静态函数,互不影响
非静态函数可以在另一个文件中直接引用,甚至不必使用extern声明
-
-
关键字const
在C语言中,const修饰的变量具有常属性,不可以修改该变量的值。
const int num=10;
int const num=10;
//变量num的值不可以被改变
#define line 10
const int num=10;
但是当它们在数组中,就会体现出区别:
int a[line]
//line 是常量,没有问题
int a[num]
//num是const修饰的具有常属性的变量,这里不可以用在数组中
指针
const 也可以用来修饰指针const int *p;
<=>int const *p;
$-这里的p是一个指向常量的指针,const修饰的是p所指向的内容,所以不能改变p指向的值,但是可以改变变量p.
int *const p;
$-这里的p是一个指向整型常量的指针,变量p不可以修改,p所指向的内容可以修改。
const int *const p;
$-这里的p不能做任何的修改函数
const也能修饰函数的形参,以保护形参不被修改
int example(const int x)