1)数组长度获取方法
int a[10] = {0};
printf("sizeof(a) = %d\n", sizeof(a));//返回40,数组 a 是 int 型的,每个元素占 4 字节,所以长度为 10 的数组在内存中所占的字节数就是 40。而总的字节数除以一个元素所占的字节数就是数组的长度。
int cnt = sizeof(a) / sizeof(a[0]);
printf("cnt = %d\n", cnt); //返回10
如果:int a[10] = {1, 2, 3, 4, 5};//sizeof(a)/sizeof(a[0])
求出的是 10,而不是 5
2)char* 之间的赋值!
绝对不能用=,如果使用等号=,就相当于这两个char* 的数据变成一个数据了,它们共同指向一个地址,也就没有了任何意义。
然是strcpy((char*)a,(char*)b)
此处需要注意,a和b绝对绝对不能为空指针,必须指向一个实体,才能这样赋值,不然会报错,会报错,会报错。
1.单字符 char a=’1’;
2.多字符 char a[5]=”x5zj”;
3.字符数不确定 char a[]=”x5zj.com”;
4.char* str = new char[11];str = “diyiot.top”;
5.char* str = “diyiot”;
6.char* str = new char[11];strcpy(str,”diyiot.top”);
7、char *p1 = "diyiot";char p2[] = "diyiot";
然而p2中的值可以改变(例:p2[1] = 'com'),若想改变p1中的值(例:p1[1] = 'com')就无法编译通过。
原因:char * 实际上定义了指向字符串的指针,由于指向的内容由编译器分配在文字常量区(存放常量字符串,程序结束后由系统释放)中,无法改变其值。
char [] 分配在栈中(由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈),其值可改变。
char *p11 = "diyiot.top"; //方法1
char *p12; //方法2
p12 = "www.diyiot.top";
char *p1;//注意理解,这是个字符串指针
char p2[] = "www.diyiot.top";//可变字符串
p2[2] = '\0'; //可改变,加个结束符
p1 = p2; //指针p1指向p2字符串。所以,这时候取值p1其实就是取p2的值,理解为赋值了。
char*动态分配:
用c语言中的(char*)malloc(sizeof(char)*n),或c++中的new char(n);
char str[10]="Hello";
char str[10]={'H','e','l','l','o','\0'};//第一种情况:初始化数据小于数组长度(多余的用 '\0'填充)
char str[10]={'H'};//
char str[10]={0}; // 这种初始化有时很常见(全部初始化为'\0' )
char str[10]="Hello, world.\n"; //第二种情况:初始化数据等于或超过数据长度(不可预期的结果)
char str[]="Hello, world.\n";//第三种情况,不指定数组长度
第一种情况:初始化数据小于数组长度
如前面两个初始化的例子,这两个例子的效果一样,前五个字符为Hello,剩余的用'\0'填充
第三行与第四行,都只指定了第一个字符,剩余的全部用'\0'填充。第四个例子相当将整个数组初始化为'\0'
第二种情况:初始化数据等于或超过数据长度
这种情况下,超出的部分将被丢弃,所以最五个例子的最后一个字符为'r'.
此时使用一些字符串函数时要特别注意,因为该数组不含有'\0',会造成不可预期的结果
第三种情况:不指定数组长度
这种情况下,数组部分如果不含有明确指定的'\0',那么编译时会被自动添加一个'\0';
在数组不含有明确的'\0'时,sizeof=strlen+1
char* str1 ="x5zj.com"
含义是先新建一个字符串,内容是abcd 然后str1是一个头指针,指向这个串. 但是这个新建串是作为const存在的,并不是一个可以编辑的变量,因此,一旦你想更改其中的值,程序就会挂掉.
相应的
char *str1 = (char*)malloc(9*sizeof(char)); //相当于开辟一个9个长度的数组,头指针是str1
str1 = "www.x5zj.com";//但是第二句又把str1指向的位置变了,之后还是不能操作str1的内容.
char str2[] = "x5zj.com";//这个的含义是在内存中开辟一个数组,然后向该数组中填充"x5zj.com", 是一个可操作的变量.所以初始化的时候可以这么写,就能在之后更改其中的内容了.
牢记:
1、char c[]="hello world"是分配一个局部数组;
2、char *c="hello world"是分配一个全局数组;
这两种方式的区别:(2)这个指针指向常量字符串,存储在静态存储区,是只读的,不能被修改。而char str[]="hello world"是一个局部变量数组,存储在栈上的内存空间,可以被修改。
如果要把一个const char*的字符串赋给char*,不能用=号,只能用strcpy
3)一些常用字符串函数
#incldue
void *memset(void *s,int c,size_t n);
size_t strlen(const char *s);
void *memcpy(void *dest,const void *src,size_t n);
void *memmove(void *dest,const void *src,size_t n);
char *strcat(char *dest,const char *src);
char *strncat(char *desk,const char *src,size_t n);
//大小写敏感
int memcmp(const void *s1,const void *s2,size_t n);
int strcmp(const char *s1,const char *s2);
int strncmp(const char *s1,const char *s2,size_t n);
//大小写不敏感
int strcasecmp(const char *s1,const char *s2);
int strncasecmp(const char *s1,const char *s2,size_t n);
//正反向查询
char *strchr(const char *s,int c);
char *strrchr(const char *s,int c);
char *strstr(const char *haystack,const char *needle);
//分割字符串
char *strtok(char *str,const char *delim);
char *strtok_r(char *str,const char *delim,char **saveptr);
char * 和char []的区别举例
char* strA()
{
char* str = "hello world";
return str;
}
char* strB()
{
char str[] = "hello world";
return str;
}
/* ************分析结果**********************
1 char* str = strA();
cout << str << endl;
2 char* str2 = strB();
cout << str2 << endl;
在主函数中strA函数,可以正常的输出"hello world"。
而调用strB时,输出则是乱码!
原因在于char* str = "hello world"中定义的str是存储在静态存储区中,具有全局属性,
所以函数结束时,能够正常返回指向存有hello world的内存单元,
而char str[] = "hello world"中的str是存储在栈上的局部变量数组,但函数运行结束时,
会清空栈的存储空间,因此,str2将指向无效的地址空间。因此是乱码.
*******************************************/
4)经典错误分析
1.char*是变量,值可以改变, char[]是常量,值不能改变。
比如:
char * a=”string1”;
char b[]=”string2”;
a=b; //OK
a=”string3”; //OK
b=a; //报错!左边操作数只读
b=”string3” //报错!左边操作数只读
解释: a是一个char型指针变量,其值(指向)可以改变;
b是一个char型数组的名字,也是该数组首元素的地址,是常量,其值不可以改变 。
2.char[]对应的内存区域总是可写,char*指向的区域有时可写,有时只读
比如:
char * a=”string1”;
char b[]=”string2”;
gets(a); //试图将读入的字符串保存到a指向的区域,运行崩溃!
gets(b) //OK
解释: a指向的是一个字符串常量,即指向的内存区域只读;
b始终指向他所代表的数组在内存中的位置,始终可写!
注意,若改成这样gets(a)就合法了:
char * a=”string1”;
char b[]=”string2”;
a=b; //a,b指向同一个区域,注意这里改变了a的指向
gets(a) //OK
printf(“%s”,b) //会出现gets(a)时输入的结果
解释: a的值变成了是字符数组首地址,即&b[0],该地址指向的区域是char *或者说 char[8],习惯上称该类型为字符数组,其实也可以称之为“字符串变量”,区域可读可写。
注意:char *本身是一个字符指针变量,但是它既可以指向字符串常量,又可以指向字符串变量,指向的类型决定了对应的字符串能不能改变。
3.char * 和char[]的初始化操作有着根本区别:
测试代码:
char *a=”Hello World”;
char b[]=”Hello World”;
printf(“%s, %d\n”,”Hello World”, “Hello World”);
printf(“%s, %d %d\n”, a, a, &a);
printf(“%s, %d %d\n”, b, b, &b);
尽管都对应了相同的字符串,但”Hellow World”的地址 和 a对应的地址相同,与b指向的地址有较大差异;&a 、&b都是在同一内存区域,且&b==b
根据c内存区域划分知识,我们知道,局部变量都创建在栈区,而常量都创建在文字常量区,显然,a、b都是栈区的变量,但是a指向了常量(字符串常量),b则指向了变量(字符数组),指向了自己(&b==b==&b[0])。
说明以下问题:
char * a=”string1”;是实现了3个操作:
1声明一个char*变量(也就是声明了一个指向char的指针变量)。
2在内存中的文字常量区中开辟了一个空间存储字符串常量”string1”。
3返回这个区域的地址,作为值,赋给这个字符指针变量a
最终的结果:指针变量a指向了这一个字符串常量“string1”
(注意,如果这时候我们再执行:char * c=”string1”;则,c==a,实际上,只会执行上述步骤的1和3,因为这个常量已经在内存中创建)
char b[]=”string2”;则是实现了2个操作:
1声明一个char 的数组,
2为该数组“赋值”,即将”string2”的每一个字符分别赋值给数组的每一个元素,存储在栈上。
最终的结果:“数组的值”(注意不是b的值)等于”string2”,而不是b指向一个字符串常量
PS:
实际上, char * a=”string1”; 的写法是不规范的!
因为a指向了即字符常量,一旦strcpy(a,”string2”)就糟糕了,试图向只读的内存区域写入,程序会崩溃的!尽管VS下的编译器不会警告,但如果你使用了语法严谨的Linux下的C编译器GCC,或者在windows下使用MinGW编译器就会得到警告。
所以,我们还是应当按照”类型相同赋值”的原则来写代码: const char * a=”string1”;
保证意外赋值语句不会通过编译。
小结
对于
const char * a=”x5zj.com”
char b[]=”diyiot.top”;
1.a是const char 类型, b是char const类型
( 或者理解为 (const char)xx 和 char (const xx) )
2.a是一个指针变量,a的值(指向)是可以改变的,但a只能指向(字符串)常量,指向的区域的内容不可改变;
3.b是一个指针常量,b的值(指向)不能变;但b指向的目标(数组b在内存中的区域)的内容是可变的
4.作为函数的声明的参数的时候,char []是被当做char *来处理的!两种形参声明写法完全等效!
5、还有一些分析参考:https://blog.csdn.net/daiyutage/article/details/8604720
总结的够多的啦,好好消化一下,不断调试测试一下结果,再分析,牢记!真没少费劲为这些知识点滴。