使用列表初始化。
1 2
char str2[] = {'h' , 'e' , 'l' , 'l' , 'o' };char str3[] = {'h' , 'e' , 'l' , 'l' , 'o' , '\0' };
使用字符串字面值初始化。
1 2
char str1[] = "helloworld" ;
这种方式是字符数组初始化的简便写法。
指针和C风格字符串
1
char *messages= "hello world" ;
对于以上三种方式来说,方式1和2是等价的,这种方式中的"helloworld"存放在栈中。而第三种有些特殊,在第三种方式中,"hello world"是一个字符串字面值常量,存放在数据区的字符串常量部分[6,7,8,9]。事实上,它是一个常量字符数组,是一个不可修改的左值[9],把它作为右值时,会进行类型转换将左值转换成右值,即使用常量字符数组首字符的地址进行初始化。
C风格字符串操作函数
C语言标准库
<string.h>
或者C++版本的
<string.h>
提供了以下的字符串操作函数,它们的参数必须是指向以空字符结束的字符数组的指针。在函数内存不会验证这些字符串参数是否满足要求。
strlen(p)
,返回p指向的字符串的长度,不包括空字符
strcmp(p1, p2)
,p1==p2,返回0,p1>p2,返回正值,否则返回负值。
strcat(p1, p2)
,p2拼接到p1,返回p1
sctcpy(p1, p2)
,p2拷贝到p1,返回p1
有一点需要注意的是,
p2
必须能够容纳下拼接后或者拷贝后的字符串,编译器不会进行检查,这需要由程序员自己进行检查。
C风格字符串的比较
两个C风格字符串的比较,其实比较的是指针而不是字符串本身。如下所示:
1 2 3
char str1[] = "hello"; char str2[] = "world"; if(str1 < str2) //这行代码比较的不是两个字符串,而是两个指针。
string
和C风格字符串的相互转换
允许使用以空字符结束的字符数组初始化
string
对象或者为
string
对象赋值。
string
对象的加法运算中允许使用空字符结束的字符数组作为其运算对象,不能两个都是。
string
对象的复合赋值运算中允许使用以空字符结束的字符数组作为其右侧运算对象。
string
转换成C风格字符串
1 2
string s("hello world!"); const char *str = s.c_str();
多维数组指的是数组的数组。多维数组的定义方式如下:
1 2
int ia[3 ][4 ]; int arr[10 ][20 ][30 ];
按照从右到左的维度顺序依次进行存储,实际上它们存储在了线性空间内。比如对于一个数组
int array[3][6]
,它在内存中的存储顺序其实是:
a[0][0], a[0][1], a[0][2], a[0][3], a[0][4], a[0][5], a[1][0], a[1][1], a[1][2], a[1][3], a[1][4], a[1][5], a[2][0], a[2][1], a[2][2], a[2][3], a[2][4], a[2][5]。
多维数组的初始化
使用花括号括起来的一组值初始化多维数组。
指定所有元素的值。
1
int a[3 ][4 ] = {0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 };
初始化一部分,其余的进行值初始化
1
int a[3 ][4 ] = {0 , 1 , 2 , 3 };
只对a[0]进行了初始化。
初始化每一行的部分元素。
1
int a[3 ][4 ] = {{0 }, {1 }, {2 }, {3 }};
只对每一行的第一个值进行了初始化。
多维数组数组名和指针
一维数组的数组名是一个指针,它的类型是指向元素类型的指针。多维数组也一样,多维数组的数组名也是一个指针,只不过它的类型是指向数组的指针。比如:
数组名
matrix
实际上是指向数组首元素的一个指针,数组首元素是一个
int [10]
类型的数组,所以
matrix
是一个指向
int [10]
类型数组的指针,解引用操作
*matrix
得到一个
int [10]
类型的数组,这个东西其实也是一个数组名,相当于一个指针,指向一个
int
类型。
*(*(matrix+1)+2)
其实就是
matrix[1][2]
。
多维数组的下标
如果要访问多维数组中的某个元素,必须指定所有维度的下标。即:
1 2 3
int matrix[3 ][10 ];matrix[1 ][15 ];
但是,实际上,下标和间接引用是等价的,在多维数组中也一样。数组名
matrix
可以当成一个指针,
matrix +1
实际上指向第二个
int [10]
的数组,
*(matrix+1)
是一个
int [10]
的数组。
指向数组的指针
在下面的代码中,
pa
是一个int指针,
p
是一个
int [10]
类型的指针,指向
matrix
的第一行的10个元素。
1 2
int array [10 ], *pa = vector ;int matrix[3 ][10 ], (*p)[10 ] = matrix;
我们可以创建指针的数组。例如:
创建了一个数组,数组有10个元素,每个元素都是一个
int*
类型的指针。可以创建一个
char*
类型的指针数组:
1 2 3 4 5 6 7 8 9
const char *keyword[] = { "do" , "while" , "if" , "for" , "return" , "switch" , NULL }
可以参考下面示意图:
数组和左值
数组本身是一个不可修改的左值。
字符串字面值和左值
字符串字面值常量是一个常量,不可修改的左值,它以数组的形式存储。
为什么数组不支持赋值
不支持数组的拷贝是为了避免不必要的复制开销,数组复制将会导致连续的内存读和写。
为什么用指针代替数组,不是因为他们太像了,而是避免赋值的开销,因为c里面只有值传递,如果对数组采用pass by value,会有很大的开销。
array
,
&array
和
&array[0]
array
和
&array[0]
是一样的,它们和&array有什么区别呢?[3]
1 2 3 4
int array [] = {1 , 2 , 3 , 4 ,5 };printf ("array=%p, &array=%p\n" , array , &array );printf ("array+1=%p: &array+1=%p\n" , array +1 , &array +1 );
程序的输出如下:
1 2
array=0x7ffc50c541c0: &array=0x7ffc50c541c0 array+1=0x7ffc50c541c4: &array+1=0x7ffc50c541d4
根据上面程序的输出,
array
和
&array
得到了一样的地址。但是它们并不是一样的!!!它们的地址相同,但是地址的类型不同。
对指针进行算术运算,将
array
和
&array
都加一,我们却得到了不同的结果。事实上,
array
是指向数组第一个元素的指针,而
&array
是指向整个
int [5]
数组的指针。因此,根据指针运算规则,对地址
array
和
&array
进行算术运算,得到了不同的结果。
数组和函数
数组有两个特殊的性质:
不允许拷贝,因为不能拷贝数组,所以不能以值传递的方式使用数组参数。
在使用数组时通常会将其转换成指针。因为数组会被转换成指针,所以为函数传递数组时,实际上传递的是指向数组首元素的指针,这样子可以节约开销。
管理数组转换的指针
当传递给函数一个数组时,实参自动的转成指向数组首元素的指针,数组的大小对于函数的调用没有什么影响。因为数组是以指针的形式传递给函数的,所以函数其实是不知道数组的大小的,调用者应该为此提供一些额外的信息。通常有三种方式:
显示传递一个表示数组大小的形参
使用标记指定数组长度,要求数组本身包含一个结束标记,典型的例子是C风格字符串。
使用标准库规范,传递数组首元素和尾后元素的指针。可以使用
begin
和
end
函数获得数组的首元素和尾后元素的指针。
数组形参和
const
当函数不需要对数组进行写操作时,数组形参应该是指向
const
的指针。只有当函数确实需要改变数组元素值的时候,才把形参定义成指向非常量的指针。
数组引用形参
C++允许将变量定义成数组的引用,形参也可以是数组的引用。此时,引用形参绑定到数组上。
传递多维数组
C++其实没有真正的多维数组,多维数组其实就是数组的数组。把多维数组传递给函数时,传递的是指向数组首元素的指针。而多维数组是数组的数组,首元素本身就是一个数组,多维数组转换成指向数组的指针。数组第二维以及后面维度都是数组类型的一部分,不能省略。
函数返回值和数组
因为数组不能被拷贝,所以函数不能返回数组。不过可以返回数组的指针或者引用。
声明一个返回数组指针的函数
返回数组指针的函数形式如下所示:
1
Type (*function(parameter_list)) [dimension]
类似于其他数组的声明,Type表示元素的类型,dimension表示数组的大小,
(*function(parameter_list))
两端的括号必须在,否则函数的返回类型就是指针的数组。如下示例:
1
int (*func(int i))[10 ];
func
带有参数,说明它是一个函数,前面带有解引用操作,说明可以对函数调用的结果执行解引用操作,括号右面说明这是一个维度为10的数组,括号左面是数组类型。
使用尾置返回类型
可以使用尾置返回类型,任何函数的定义都能使用尾置返回,但是这种形式一般用于比较复杂的返回类型,比如数组的指针或者数组的引用。形式如下:
1
auto func() -> int (*)[10];
使用
decltype
可以使用
decltype
声明返回值类型。比如返回一个指针时,
1 2 3 4 5 6 7
int odd[] = {1 , 3 , 5 , 7 , 9 };int even[] = {2 , 4 , 6 , 8 , 10 };decltype (odd) *func(int i){ return (i%2 )? &odd: &even; }