C- 总结篇

1.  字符数组

  • 1) char str[]={'a','b','c'} ; // str 不包含‘\0’ 结束符
  • 2) char str[3]={'a','b','c'} ;//与上面相同意思,不会报错,请注意与 5) 的区别
  • 3) char str[5]={'a','b','c'}; // str 包含‘\0’ 结束符,str[4],str[5] 存的内容均为‘\0’
  • 4) char str[]="abc";//字符串形式赋值,结尾都包含‘\0’
  • 5) char str[3]="abc";//报错,长度越界。
  • 6) char str[5]="abc";//与3) 等价。

2. 输入流 getchar ,scanf,gets

  • 1) scanf函数在输入整型数据时不能接受空格、制表符Tab、回车等
  • 2) scanf 函数在输入字符型数据时,不接受空格,制表符tab,但接受回车enter 。
  • 3)scanf从输入流缓冲区中提取数据,并非在键盘缓冲区里面提取数据。 当我们用键盘输入一个a的时候,然后会按enter,在按enter之前,a在键盘缓冲区,而输入流缓冲区里面并没有东西,getchar处于等待状态。当我按下enter之后,字符a和enter都进入输入流缓冲区,getchar开始读取一个字符a,然后再读取一个enter,输入流缓冲区已经没有东西了。此时我们也输出了。
  • .4)scanf()以Space、Enter、Tab结束一次输入,不会舍弃最后的回车符(即回车符会残留在缓冲区中)。
  • 5) getchar()以Enter结束输入,也不会舍弃最后的回车符。 
  • 3) gets函数能够接受空格、制表符Tab和回车等
  • 4)  scanf函数如果输入了空格(空格键和tab键)会认为字符串结束,空格及后面的字符将作为下一个输入项处理
  • 5) gets()函数将接收输入的整个字符串直到遇到换行为止,会舍弃最后的回车符!

3. 越界,长度问题

  • 1) strcpy(buffer,str);如果str比buffer 长度长,那么就会影响buffer 相邻变量的内存存储结构。
  • 2) 数组作为形参,void strss(int a[]) ,sizeof(a) =4, 此时的a是指针类型。不是数组。
  • 3) char arr[] = "abcd" ; //strlen(arr) =4, sizeof(a)=5。

4. 函数与指针

  • 1) float *f1();//  这是个指针函数,即f1是函数,返回的是一个指向浮点类型的指针。
  • 2) float (*f2)();// 这是个函数指针,即f2是指针,指向一个返回值为浮点类型的函数。
  • 3) (float (*)())   // 这是个类型转换符,由2) 演变而来,表示一个“指向返回值为浮点类型的函数的指针” 。
  • 4) 类型转换符演变:去掉声明中变量名和声明末尾的分号,再将剩余的部分用一个括号整个“封装”起来。
  • 5) (void (*)())0  //表示  将整型 0 强制转换成一个 “指向返回值为 void 的函数的指针”,其中 0 是一个地址,也就是说,一个函数存在首地址为 0 的一段区域内
  • 6) (*0)();   //同2) ,这也是一个函数指针,指针变量名是 0 ,指向一个入口地址是0 的函数。但指针变量不能为一个常数,需要用5)经过强转后的 0来替换这里的  0,否则该式 报error。
  • 7) (*(void(*)())0)(): //意义同6),将5)替换6)中的0的结果。实现对存储在起始地址为 0 的一段内存上的函数的调用。

5. 结构体与指针

    https://blog.csdn.net/weixin_42167759/article/details/80330953

6. memset()函数及其作用

  • 可以方便的清空一个结构类型的变量或数组:char a[100];memset(a, '/0', sizeof(a));

struct sample_struct
  {
  char csName[16];
  int iSeq;
  int iType;
  };

        memset(&stTest,0,sizeof(struct sample_struct));//有个问题,如果第二参数不是 0,就不行,why?

请看:

如下demo是可以的,能把数组中的元素值都设置成字符1
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
     char a[5];
     memset(a,'1',5);
     for(int i = 0;i < 5;i++)
       cout<<a[i]<<"   ";
     system("pause");
     return 0;
}
而,如下程序想吧数组中的元素值设置成1,却是不可行的
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
     int a[5];
     memset(a,1,5);//这里改成memset(a,1,5 *sizeof(int))也是不可以的
     for(int i = 0;i < 5;i++)
       cout<<a[i]<<"   ";
     system("pause");
     return 0;
}
问题是:

1,第一个程序为什么可以,而第二个不行,
2,不想要用for,或是while循环来初始化int a[5];能做到吗?(有没有一个像memset()这样的函数初始化)

答:

1.因为第一个程序的数组a是字符型的,字符型占据内存大小是1Byte,而memset函数也是以字节为单位进行赋值的,所以你输出没有问题。而第二个程序a是整型的,使用memset还是按字节赋值,这样赋值完以后,每个数组元素的值实际上是0x01010101即十进制的16843009。你看看你输出结果是否这样?

2.如果用memset(a,1,20);
就是对a指向的内存的20个字节进行赋值,每个都用ASCII为1的字符去填充,转为二进制后,1就是00000001,占一个字节。一个INT元素是4字节,合一起就是1000000010000000100000001,就等于16843009,就完成了对一个INT元素的赋值了。

  • 与memcopy,strcopy 区别:

     

          memset()的深刻内涵:用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为‘ ’或‘/0’;         例:char    a[100];memset(a, '/0', sizeof(a));

             memcpy用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度;例:char a[100],b[50]; memcpy(b, a, sizeof(b));注意如用sizeof(a),会造成b的内存地址溢出。

            strcpy就只能拷贝字符串了,它遇到'/0'就结束拷贝;例:char a[100],b[50];strcpy(a,b);如用strcpy(b,a),要注意a中的字符串长度(第一个‘/0’之前)是否超过50位,如超过,则会造成b的内存地址溢出。

7. 指针应用相关问题

  • 指针相减,结果是一个常量,而不是指针型变量。如两个“int*”型的指针变量相减,结果是 int 型常量。此时要是把相减的结果赋给“int*”型就会报错。而且两个指针变量相减的结果是这两个地址之间元素的个数,而不是地址的个数。
  •  

8. const 关键字作用及用法

  • 用 const 定义的变量的值是不允许改变的,即不允许给它重新赋值,即使是赋相同的值也不可以。所以说它定义的是只读变量。这也就意味着必须在定义的时候就给它赋初值。
  • 用 const 修饰的变量,无论是全局变量还是局部变量,生存周期都是程序运行的整个过程
  • 局部变量存储在栈中,静态变量存储在静态存储区中,而经过 const 修饰过的变量存储在内存中的“只读数据段”中。只读数据段中存放着常量和只读变量等不可修改的量。
  • const 定义的变量仍然不能作为数组的长度。但是需要注意的是,在 C++ 中可以!C++ 扩展了 const 的含义,在 C++ 中用 const 定义的变量也可作为数组的长度。

9. conscst VS define

  • 很多人在学习 const 的时候都会混淆它与 define 的区别。从功能上说它们确实很像,但它们又有明显的不同:
  • define是预编译指令,而const是普通变量的定义。define定义的宏是在预处理阶段展开的,而const定义的只读变量是在编译运行阶段使用的。
  • const定义的是变量,而define定义的是常量。define定义的宏在编译后就不存在了,它不占用内存,因为它不是变量,系统只会给变量分配内存。但const定义的常变量本质上仍然是一个变量,具有变量的基本属性,有类型、占用存储单元。可以说,常变量是有名字的不变量,而常量是没有名字的。有名字就便于在程序中被引用,所以从使用的角度看,除了不能作为数组的长度,用const定义的常变量具有宏的优点,而且使用更方便。所以编程时在使用const和define都可以的情况下尽量使用常变量来取代宏。
  • const定义的是变量,而宏定义的是常量,所以const定义的对象有数据类型,而宏定义的对象没有数据类型。所以编译器可以对前者进行类型安全检查,而对后者只是机械地进行字符替换,没有类型安全检查。这样就很容易出问题,即“边际问题”或者说是“括号问题”。

10.const修饰指针的三种效果:

  •  const int*p=&a: 表示*p 不可变。*p 表示的是指针变量 p 所指向的内存单元里面的内容。此时这个内容不可变。其他的都可变。(ps:虽然在 *p 前加上 const 可以禁止指针变量 p 修改变量 a 中的值,但是它只能“禁止指针变量 p 修改”。也就是说,它只能保证在使用指针变量 p 时,p 不能修改 a 中的值。但是我并没有说 const 可以保护 a 禁止一切的修改,其他指向 a 的没有用 const 修饰的指针变量照样可以修改 a 的值,而且变量 a 自己也可以修改自己的值)
  • int*const p=&a:表示p不可变。p 中存放的内存单元的地址不可变,而内存单元中的内容可变
  • const int*const p=&a:表示p 跟*p 均不可变。 p 中存放的内存单元的地址和内存单元中的内容都不可变。

11.const VS define

  1. define是预编译指令,而const是普通变量的定义。define定义的宏是在预处理阶段展开的,而const定义的只读变量是在编译运行阶段使用的。
  2. const定义的是变量,而define定义的是常量。define定义的宏在编译后就不存在了,它不占用内存,因为它不是变量,系统只会给变量分配内存。但const定义的常变量本质上仍然是一个变量,具有变量的基本属性,有类型、占用存储单元。可以说,常变量是有名字的不变量,而常量是没有名字的。有名字就便于在程序中被引用,所以从使用的角度看,除了不能作为数组的长度,用const定义的常变量具有宏的优点,而且使用更方便。所以编程时在使用const和define都可以的情况下尽量使用常变量来取代宏。
  3. const定义的是变量,而宏定义的是常量,所以const定义的对象有数据类型,而宏定义的对象没有数据类型。所以编译器可以对前者进行类型安全检查,而对后者只是机械地进行字符替换,没有类型安全检查。这样就很容易出问题,即“边际问题”或者说是“括号问题”。

11. 数组与指针

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

  •  int *p = a:  p+i 和 a+i 等价,可以p++,但不能a++。只有变量才能进行自增和自减,常量是不能进行自增和自减的。a 代表的是数组的首地址,是一个常量,所以不能进行自增,所以不能写成a++。
  • int *p=NULL; p=&a[0]: p+1 的本质是移到数组下一个元素的地址,即&a[1]
发布了29 篇原创文章 · 获赞 27 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/linzihahaha/article/details/85049969
C-