$总结--1(C)

  • 结构的存储分配

    结构内部包含了大量的未用空间

    编译器按照成员列表的顺序一个接一个地给每个成员分配内存,只有当存储成员是需要满足正确的边界对齐要求时,成员之间才有可能出现用于填充的额外内存空间.
    若结构体为:

    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 2

    2.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;
}
  1. a作为函数参数,为int型指针 (int *)
  2. sizeof(a) 返回整个数组的长度 (int [3][4])
  3. &a 为数组指针,( int(*)[3][4] )
  4. 在表达式中,它的类型为int (const*)[4],==>指向数组首元素a[0]的常量指针,a[0]=>int[4]
  5. a=(a+0)=*(0+a)=a[0]=0[a] -------> a=&a[0]
  6. a+1 ===&a[0]+1(跨过一整行元素)
  7. a[0]+1===&a[0][1] (跨过一个元素)
  8. &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);
    

    标准库还包含了一些函数,以一种不同的方式处理字符串。这些函数接受一个显示的长度参数,用于限定进行复制或比较的字符串,可以防止难以预料的长字符串从它们的目标数组溢出。

    如果原参数和目标参数发生重叠,strncpystrncat的结果就是未定义的。

    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字节时不会停止操作

    memcpys2的起始位置复制length个字节到s1的内存起始位置,并且可以复制任何类型的值。(若s1s2出现重叠,则函数的结果未定义)
    memmove函数执行相同的功能,但它能够正确处理源参数和目标参数出现重叠的情况。
    memcmp函数比较两个序列的字节。
    memchr函数在一个字节序列中查找一个特定的值。
    memset函数把一序列字节初始化为一个特定的值。


  • #define替换

    • 在程序中扩展#define定义符号和宏时,需要涉及几个步骤
    1. 在调用宏时,首先对参数进行检查,看看是否包含了任何由#define定义的符号。如果是,它们首先被替换。
    2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被它们的值所代替。
    3. 最后,再次对结果文本进行扫描,看看它是否包含了任何由#define定义的符号。如果是,就重复上述过程。

    在用#define定义时斜杠(“\”)是用来续行的

    • “#”用来把参数转换成字符串,是给参数加上双引号
    • "##"则用来连接前后两个参数,把它们变成一个参数
    • “#@”是给参数加上单引号

  • static关键字

    在这里插入图片描述
    • 全局变量
      全局变量定义在函数体外部,在全局数据区分配存储空间,且编译器会自动对其初始化。

      普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量)。

      静态全局变量仅对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。

      在定义不需要与其他文件共享的全局变量时,加上static关键字能够有效地降低程序模块之间的耦合,避免不同文件同名变量的冲突,且不会误使用。

    • 局部变量
      在任何一个函数内部定义的变量(不加static修饰符)都属于局部变量。编译器一般不对普通局部变量进行初始化,所以 它的值在初始化时是不确定的,除非对其赋值。
      普通局部变量存储于进程栈空间,使用完毕会立即释放
      静态局部变量使用static修饰符定义,即使在声明时未赋初值,编译器也会把它初始化为0。且静态局部变量存储于进程的全局数据区,即使函数返回,它的值也会保持不变。
      变量在全局数据区分配内存空间;编译器自动对其初始化
      其作用域为局部作用域,当定义它的函数结束时,其作用域随之结束

    • 函数
      函数的使用方式与全局变量类似,在函数的返回类型前加上static,就是静态函数。

    1. 静态函数只能在声明它的文件中可见,其他文件不能引用该函数
    2. 不同的文件可以使用相同名字的静态函数,互不影响
      非静态函数可以在另一个文件中直接引用,甚至不必使用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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值