首页澳门新葡亰官方网站 › 澳门新葡亰官方网站C 入门 第八节 指针

澳门新葡亰官方网站C 入门 第八节 指针

对于一个数组,只能做两件事:确定该数组的大小以及获得指向该数组下标为0的元素的指针。其他有关数组的操作,都是通过指针进行的,任何一个数组下标运算都等同于一个对应的指针运算。

// 使用指针修改
    *p = 300;
    *q = 3.5;
    *o = 'c';
    
    printf("b = %.2f *q = %.2f n",b ,*q);
 
//    & 和 * 是配套使用的,互为逆运算
//  & 获取变量存储单元地址
//  * 通过地址访问变量的存储单元

几种不同指针的定义

char **p:指向字符指针的指针,其实与字符指针数组 char *argv[]是一样
    char a1 = 'a';
    char *a[] = { "str1", "str2", &a1 };
    char **p = a;
    printf("%cn", **(p+2));//a
 
int (*p)[3]:指向具有3列的二维整型数组的指针。
    int a[2][3] = { { 1, 2, 3 }, { 4, 5, 6 }
};
    int (*p)[3] = a;
    printf("%dn", **++p);//4
 
int *p[3]:具有3个元素的一维整型指针数组。这里的3可以省略
    int b1[] = { 1 };
    int b2[] = { 2, 3 };
    int b3 = 4;
    int *b[] = { b1, b2, &b3 };
    printf("%dn", **(b+1));//2
 
void * comp():返回一个空指针类型的函数。
void * comp() {
    char c = 'a';
    return &c;
}
int main(int argc, char * argv[]) {
    printf("%cn",*((char*)comp()));//a
    return 0;
}
 
void * (*comp)():指向返回类型为空指针的函数的指针。
void * comp() {
    char c = 'a';
    return &c;
}
void * v(void * (*comp)()){
    return (*comp)();
}
int main(int argc, char * argv[]) {
    printf("%cn",*((char*)v(comp)));
    return 0;
}
 
char (*(*x())[])():x是一个函数,它返回一个指针,该指针指向一个一维数组,该一维数组的元素为指针,这些指针分别指向多个函数,这些函数的返回值为char类型。
 
char (*(*x[3])())[5]
 
int              整型
int *             指向整型的指针
int *[3]          包含3个指向整型的指针的数组
int (*)[]        指向未指定元素个数的整型数组的指针
int *()           未指定参数、返回指向整型的指针的函数
int (*[])(void)   一个数组,其长度未指定,数组的元素为指向函数的指针,该函数没有参数且返回一个整型值
 

列表初始化 or 值初始化?

如果用的是圆括号,可以说提供的值是用来构造(construct)或者说值初始化(value
initialize)
vector对象的。

  vector<int> v1(10); //v1有10个元素,每个元素值为0【构造】

  vector<int> v2(10,1); //v2有10个元素,每个元素值为1【构造】

如果用的是花括号,可以表述成我们想要列表初始化(list
initialize)
该vector对象。

  当使用了花括号时,编译器会尽量先尝试是否能进行合法的列表初始化(初始化列表中的值要符合vector中元素的类型):

  例如:

  vector<int> v3{10}; //v3有1个元素,该元素值为10【列表初始化】

  vector<int> v4{10, 1}; //v4有2个元素,值分别是10和1【列表初始化】

  vector<string> v5{"hi"}; //合法;v5有一个元素"hi"【列表初始化】

  vector<string> v6("hi");
//非法;不能使用字符串字面值构建vector对象(显然,字符串字面值本身不是vector对象)【构造(失败)】

  vector<string> v7{10};
//合法;显然不能用int初始化string对象,所以无法进行列表初始化;但是,接下来编译器会尝试用值初始化的方式初始化vector对象,最后v7有10个默认值初始化的元素(10个空串)【列表初始化->值初始化】

    //也就是说,上一条语句实际被转换成:vector<string> v7(10)

  vector<string> v8{10, "hi"};
//合法;列表的第一个值不是string,所以无法进行列表初始化;但是,接下来编译器会尝试用值初始化的方式初始化vector对象,最后v8有10个值为"hi"的元素【列表初始化->值初始化】

    //也就是说,上一条语句实际被转换成:vector<string>
v8(10, "hi")

  补充:

  vector<string> v9{"hi", 10};
//非法;无法进行列表初始化(元素类型不符合),也无法转换成值初始化(vector<string>
v9("hi", 10)是无意义的操作)

 

如果循环体内部包含有向vector对象添加元素的语句,则避免使用范围for循环。

原因:向容器中添加元素或从容器中删除元素的操作可能会使指向容器元素的指针、引用或迭代器失效。

注意:但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素。

 

p的值是一个指向'x'、'y'、'z'
和''4个字符组成的数组的其实元素的指针。因此,执行语句q = p;后,p
和q是两个指向内存中同一个地址的指针,这个赋值语句并没有同时复制内存中的字符。

// 字符指针作为实参
//    int *p = a;
//    func1(p,5);
//    func2(p, 5);

int (*a)[3]、int *a[]区别

    //多维数组只有第一维可以省略,第三个元素没有指定内容,这里会自动初始化为0
    int a[][3] = { { 11, 12 }, { 21, 22, 23 }
};
    //可以有以下四种方式访问a[1][0]元素,方括号[]比*优
    //先级高,所以下面是可以省略的
    printf("1、%dn", a[1][0]);//21
    printf("2、%dn", *((a + 1)[0]));//21
    printf("3、%dn", *(a[1]));//21
    printf("4、%dn", **(a + 1));//21
 
    //在定义时可以不指定指针移动的单位,即指针单位长度
    //但如果不指定,则不能对指针进行移动操作,否则编译出错
    int (*p1)[] = a;
    //在不移动指针的条件下可以访问第一个元素
    printf("5、%dn", **p1);//11
    //编译不能通过,因为上面在定义p1时未指定指针的最小移动单位
    //所以不能移动
    //  printf("%d", **(p1 + 1));
 
    //一维数组指针
    int b[]={1,2,3};
    int *pb=b;
    printf("6、%dn", *(pb + 1));//2
 
    //指定长度后就可以对指针进行移动
    int (*p2)[3] = a;
    //以3个int为单位进行移动
    printf("7、%dn", **(p2 + 1));//21
 
    //编译时警告。以2个int单位进行移动
    int (*p3)[2] = a;
    printf("8、%dn", **(p3 + 1));//0
 
int
(*a)[3]本质是指针变量,它指向一个具有3列的某个整型数组,3是指该指针移动的基本单位是3格,至于每格多少位要看声明时类型,这里是int,所以指针在向前或向后移动时是以3个整型为单位移动。
 
int
*a[]本质是一个一维数组,数组里的每个元素是一个地址,该地址可以是一个整型数组的地址,也可以是一个整型变量的地址。
//这里方括号[]中的长度可以省,这与 char (*a)[]类型的指针不同,因为每次指针移动一个char类型已明确
void val(char *a[], int *b[]) {
    //这里的*(a[1]+3)与*(b[1]+1)不能写成*(a[1][3])与*(b[1][1]),因为a与b不是一个二维数组
    printf("%s %c %c %d %dn", a[1], *(a[1] + 3), *a[2], *(b[1] + 1),
*b[2]);//cegi i a 3 4
    //取第一个字符串中的第二个字符
    printf("%cn", *++(a[0]));//m
    //取第二个字符串中的第一个字符:a先加1,再取第二个字符串,最后取第二个串中的第一个字符
    printf("%cn", (*++a)[0]);//c
    //取第二个字符串中的第2个字符:a[0]为第二个字符串地址,++a[0]第二个字符串首地址下移,*++a[0]取出相应地址中的字符
    printf("%cn", *++a[0]);//e
    //指针a再次向下移动一个单元,此时指向了第三个元素,它是一个字符
    printf("%cn", **++a);//a
    //注,这里 ++b 没有问题,而main函数中不能,这主要是因为这里的b指针是在原指针的基础上
    //重新拷贝的一份,所以可以随时改变它的指向
    printf("%dn", **++b);//2
}
 
int main(int argc, char * argv[]) {
    char a1 = 'a';
    //字符串使用字符数组来存储
    char a2[]={'c','e','g','i',''};
    //可以直接赋值一个常量字符串,或是一个字符数组变量,或是一个字符变量的地址
    char *a[] = { "kmuw", a2, &a1 };
 
    int b1[] = { 1 };
    int b2[] = { 2, 3 };
    int b3 = 4;
    int *b[] = { b1, b2, &b3 };
 
    //编译出错,因为b是一个一维的指针数组,b是数组名,所以不能
    //修改其指向,但传递给其他函数后又可以修改,比如这里的 val 函数里
    //printf("%dn", **(++b));
    val(a, b);
    return 0;
}
 

多维数组的下标引用

int a[3][4];

int (&row)[4] = a[1]; //把row绑定到a的第2个“4元素数组”上

注解:row是一个引用,绑定到的对象是一个包含4个元素的数组,数组元素的类型是int整数。

 

使用范围for语句处理多维数组

int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};

for(auto &row : a)
  for(auto &var : row)
    var += 1;

澳门新葡亰官方网站,for(auto &row : a)
{
  for(auto &var : row)
  {
    cout << var<<" ";
  }
  cout << endl;
}

输出:

1 2 3 4
5 6 7 8
9 10 11 12

注解:第一层循环,变量row引用(绑定到)了二维数组a的每一行(这一行是一个一维数组);第二层循环,变量var引用(绑定到)了某一行(二维数组中的某一个一维数组)中的某一个元素,并通过引用令其值修改为原来的值+1。

 

注:使用范围for处理多维数组,除了最内层循环,其余所有循环的控制变量都应该是引用类型。(不论循环中是否对数组有写操作)

原因:

看看如果不声明成引用类型会发生什么:

for(auto row : a)
  for(auto var : row)

像之前一样,第一层循环遍历a的所有元素,这些元素实际是大小为4的数组;

因为row不是引用类型,所以编译器初始化row时会自动将这些数组形式的元素(和其它数组类型一样)转换成指向该数组内首元素的指针,这样得到row的类型就是int*;

到了内层循环,编译器试图“在一个int*(一个指针)内遍历”,这显然是不合法的。

 

 

void changeValue2(int *p1,int *p2)
{
    int temp = *p1;
    *p1 = *p2;
    *p2 = temp;
}

初始化...
1
指针...
1
指针与地址...
3
指针与数组...
4
指针数组、数组指针...
5
澳门新葡亰娱乐官网,int (*a)[3]、int *a[]区别...
6
几种不同指针的定义...
8
 

多维数组

多维数组实际就是数组元素为数组的数组。

 

    
    int a = 100;
    int *p = &a;
    printf("%p ",p);
    printf("%p ",p + 1);
    printf("%p n",p + 2);
    
    //第二种: p - n
    表示: 从p指向的存储单元向低位偏移n个数据类型的字节数(n *
数据类型的字节数)
    
    第三种: p ++ 或者 ++ p
    指针向高地址移动,移动的距离是指针指向数据类型所占的字节数
    p的指向改变了

初始化

在不进行显示初始化时,外部变量与静态变量都将被初始化为0,而自动变量和寄存器变量的初值则没有定义。
 
对于外部变量与静态变量来说,初始化表达式必须是常量表达式,且只初始化一次(在程序开始执行前进行初始化)。对于自动变量与寄存器变量,则在每次进行函数或程序块时都将被初始化。
 
在定义数组时,可以指定数组大小的同时又初始化内容,也可只指定元素内容,则不指定大小,这时数组的大小是初始化元素的个数。
 
在既指定大小又指定内容时,指定的大小可以大于初始化元素个数,没有初始化的元素的内容为随机数。但指定的大小不能小于后面初始化的元素的个数,否则编译时会有警告,并且即使初始化了超过指定大小的元素内容,访问时得到的也是随机值。
 
如果初始化元素个数比指定的数组大小小时(但一定要初始化,那怕初始化时没有内容,只声明不初始时元素的值还是一个随机值),则没有初始化的表达式元素将被初始化为0,如:
    int a[4] ={};//每个元素的值为0
    int b[4]={1};//除第一个元素外,其他三个元素的值都是0
    int c[4];//所有元素都是随机值
 
字符数组的初始化有以下同人种方式:
    char str[] = "string";
    char str[] = { 's', 't', 'r', 'i', 'n', 'g', '' };

定义迭代器

vector<T>::iterator it1;

vector<T>::const_iterator it2;

const_iterator类似于常量指针,能通过它读取但不能修改它所指的元素值(iterator则可读可写)。

如果vector对象(或其它容器类的对象)是一个常量,只能用const_iterator。否则,可用const_iterator或iterator

 

任何一个逻辑运算符的优先级低于任何一个关系运算符。

    strs本质是一维数组
    strs存储的数组元素是字符指针(字符指针指向常量字符串)
    strs[0]第一个字符指针,指向常量区的"iOS"
    strs[1]第二个字符指针,指向常量区的"iPad"
    strs[2]第三个字符指针,指向常量区的"iMac"

指针

指针是把其他变量地址作为其值的变量。变量通常直接包含一个具体的值,而指针包含的是拥有具体值的变量的地址。变量名直接引用了一个值,而指针是间接地引用了一个值。
澳门新葡亰官方网站 1
countPtr是一个指向整数值的指针
 
可以把指针初始化为0、NULL或某个地址。具有值NULL的指针不指向任何值。NULL是在头文件<stdio.h>中定义的符号常量。把一个指针初始化为0等价于把它初始化为NULL。当把**0赋值给指针时,编译器先把0转换为指向合适数据类型的指针**0是唯一能够直接赋给指针变量的整数值
 
    int y;
    int * yPtr;
    y = 7;
    yPtr = &y;
    printf("%p %pnn", &y, yPtr);//0022FF4C 0022FF4C
    printf("%d %dnn", y, *yPtr);//7 7
    printf("%p %p %pnn",&yPtr, &*yPtr, *&yPtr);//0022FF48 0022FF4C 0022FF4C 
澳门新葡亰官方网站 2
 
地址运算&的操作数必须是一个变量,不能把地址运算符用于常量、表达式或存储类别被声明为register的变量上。
 
引用没有被正确初始化或没有指向具体的内存单元都会导致致命执行错误或意外地修改重要的数据。
 
C语言中数组是以引用的方式传递的,而结构是以传值方式传递。数组是用同一个名字存储许多具有相同类型的相关数据项聚合数据类型,结构是用同一个名称存储许多不同类型的相关数据项。
 
用指向常量数据的指针传递诸如结构一样的大型对象可获得传引用调用的性能和传值调用的安全性。
 
如果两个指针类型相同,那么可以把一个指针赋给另一个指针,否则必须用强制类型转换运算符把赋值运算符右边指针的类型转换为赋值运算符左边指针的类型(一般编译时出现警告,但如果是不同类型的非指针变量,如将一个float 赋值给一个int时不会出现警告)。指向void类型的指针(即 
void *)是个例外情形,它可以表示任何类型的指针。任何类型的指针都都可以赋给指向void类型的指针,指向void类型的指针也可以赋给任何类型的指针。这两种情况都不需要使用类型转换。
 
一个指向void类型的指针仅仅包含了不知道数据类型的地址,编译器不知道该指针到底要引用多少字节,编译器必须知道数据类型后才能确定某个特定的指针要引用的字节数,因些,对于void类型的指针,编译器不能根据类型确定它引用字节数,所以除了不能引用void类型指向外,还不能对它进行指针相关的运算。
 
指针比较常用来判断某个指针是否是NULL,除非是指向同一数组,指针运算是没有意义的。
 
 
指针可以转换为整型,但此整型必须足够大。整型对象可以显式地转换为指针。这种映射总是将一个足够宽的从指针转换来的整数转换为同一个指针
 
指向某一类型的指针可以转换为指向另一类型的指针,但是,如果该指针指向的对象不满足一定的存储对齐要求,则结果指针可能会导致地址异常。指向某对象的指针可以转换为一个指向具有更小或相同存储对齐限制的对象的指针,并以保证原封不动地再转换回来。
 
指针可以转换为void
* ,并可以再原封不动的转换回来
 
指向任何对象的指针都可以转换为void*类型,且不会丢失信息。如果将结果再转换为初始指针类型,则可以恢复初始指针。执行指针到指针的转换时,一般需要显式的强制转换,这里所不同的是,指针可以被赋值为void*类型的指针,也可以赋值给void* 类型的指针,并可与void* 类型的指针进行比较

迭代器

数组作为函数参数毫无意义,C语言会自动地将作为参数的数组声明转换为相应的指针声明。即
int strlen(char s[]){} 与 int strlen(char *s){}
完全相同。但是如果一个指针参数并不实际代表一个数组,即使从技术上而言是正确的,采用数组形式的记法常会起到误导的作用。如果一个指针参数代表一个数组,则两种写法完全等价,如:
main (int argc,char * argv[]) 与 main (int argc,char **
argv)完全等价。

 */
  //  指针与函数 指针作为函数的参数
    
    int num1 = 30;
    int num2 = 90;
    changeValue(num1, num2);
    printf("%d %dn",num1,num2);
    changeValue2(&num1, &num2);
    printf("%d %dn",num1,num2);

指针数组、数组指针

指针数组(本质是数组)(char
*c[3]):数组里存储的是指针,就像整型数组一样(里面存储的是整型数据),下面是字符指针数组结构图: 
澳门新葡亰官方网站 3
数组指针(本质是指针)(指向的是一个数组):int (*a)[3] ,这种声明表明是a是一个指针,它指向具有3个整型元素的一维数组(指针移动时以3个整型为单位进行移动,可以将a看作是二维的整型数组,所以数组的大小不能省略),因为方括号[]的优先级高于*的优先级,所以上述声明中必须使用圆括号: 
澳门新葡亰官方网站 4
如果去掉圆括号,则声明成 int *a[3] (此时是一个指针数组),这相当于声明了一个数组,该数组有3个元素,其中每个元素都有一个指向某个整型变量的指针(数组变量本身是指向首元素,指针移动时以1个整型为单位进行移动,a是一个一维的整型数组): 
澳门新葡亰官方网站 5
 
指针数组的一个重要优点在于,数组的每一行长度可以不同。比如存放具有不同长度的字符串。
 
当一个指针加上或减去一个整数时,指针并非简单地加上或减去该整数值,而是加上该整数与指针引用的对象大小的乘积,而对象大小(字节数)取决于对象的数据类型。
 
因为除了数组元素外,我们不能认为两个相同类型的变量是在内存中连续存储的,所以指针算术运算除了用于数组外没有什么意义。
 
 

使用getline读取一整行

string line;

getline(cin, line);

若成功读取一行,getline函数返回true(读到空行也返回true,因为成功读到一个'n';此时line为空字符串),读取失败返回false。

 getline读取遇到换行符为止,换行符也被读进来了,但在把字符串存入string对象时,换行符没有存进去。

 

 

//总结:
指向数组的指针可以访问和修改数组元素;指向常量字符串的指针只能访问不能修改.
    
//练习 : 通过指针计算字符串长度
    char str[] = "iphone";
    char *p = str;
    int n = 0;
    while (p[n] != '') // *(p+n)!=''
    {
        n ++;
    }
    printf("%dn",n);
*/
    
#pragma mark -------------指针数组---------------
/*
//  二维数组存储 字符串 数组的数组
    char strs[3][5] = {"ios","ipad","imac"};
// 
strs存储的数组元素是字符数组(字符数组中存储的是字符串,常量字符串的副本)
    printf("%sn",strs[0]);
//strs[0]第一个字符数组的首元素地址.可以访问字符数组
    strcpy(strs[0], "mac");
    printf("%sn",strs[0]);
// 指针数组 数组中的元素都是指针
    char *strs[3] = {"iOS","iPad","iMac"};

指针与地址

&符号可用来取一个变量的地址。地址运算符只能应用于内存中的对象,即变量与数组,它不能用于表达式,常量(如 &1、&"str",但可直接将字符串赋值给一个char类型的指针,因为在C中字符串其实就是一个字符数组,而数组变量本身就是一个指向首元素的指针)或register类型的变量。
 
*星号用于取地址中的内容。
 
指向void类型的指针可以存放指向任何类型的指针(编译时好像只出现警告,没有出现错误),但它不能间接引用其自身。
 
一元运算符*和&的优先级比算术运算符的优先级高。
 
++*p与 (*p)++ 效果相当,语句中的圆括号是必需的,否则是地址先加一,而不是对象加一。类似于*和++这样的一元运算符遵循从右到左的结合顺序。
 
[]的优先级高于*的优先级。
 
指针参数使得被调用函数能够访问和修改主调函数中对象的值,比如交换两个变量值的函数其参数就应该定义成指针形式。

定义和初始化内置数组

a[size] size必须是一个常量表达式。

数组的元素会被默认初始化。和内置类型的变量一样,如果在函数内部定义了某种内置类型的数组,默认初始化会令数组元素含有未定义的值。

定义数组时需明确指出数组的类型,不允许用auto关键字由初始值的列表推断类型。

数组元素应为对象,不存在引用的数组。

 

列表初始化数组

int a1[3] = {0,1,2}; //ok

int a2[] = {0,1,2}; //ok;编译器会推断数组大小

int a3[4] = {0,1,2};
//ok;但下标为3的位置的值未显式指定,将执行默认初始化(int是内置类型;函数体外:0,函数体内:未定义)

string a4[4] = {"hi", "bye"}; //ok;等价于a4[] = {"hi", "bye", "",
""}

int a5[2] = {0,1,2}; //非法;初始值过多

 

字符数组的特殊性

对字符数组,有一种额外的初始化形式,可用字符串字面值。

char a1[] = {'C', '+', '+'};

char a2[] = {'C', '+', '+', ''};

char a3[] = "C++"; //将自动在a3中添加''

char a4[3] = "C++"; //错误;没有空间存放''

 

不允许用一个数组初始化另一个数组或把一个数组赋给另一个数组。

int a[] = {0,1,2};

int a2[] = a; //错误

a2 = a; //错误

注:也有一些编译器支持数组的赋值,这就是所谓的“编译器扩展(compiler
extension)”,但要避免使用这种非标准特性(考虑程序的可移植性)。

 

复杂的数组声明

简单的数组声明,向以前一样从右向左理解其含义比较简单:

  例如:

  int *ptrs[10]; //ptrs是含有10个整型指针的数组

  从右向左:首先,由[10]知道是一个大小为10的数组;它的名字是ptrs;然后知道数组中存放的是指向int的指针。

  注:int &ptrs[10]; //非法;不存在引用的数组,数组元素应该是对象

要想理解复杂的数组声明的含义,最好从数组的名字开始按照由内向外的顺序阅读。

  例如:

  int (*pArr)[10] = &arr; //pArr是指针,指向一个含有10个整数的数组

  由内向外:*pArr说明pArr是一个指针;接下来看右边,可知pArr是一个指向大小为10的数组的指针;最后看左边,可知数组中的元素是int。

  int (&rArr)[10] = arr; //aArr是引用,引用一个含有10个整数的数组

  由内向外:&rArr说明rArr是一个引用;再看右边,可知rArr引用的是一个大小为10的数组;最后看左边,可知数组中的元素是int。

  

  补充:

  int *(&rArr)[10] = ptrs; //rArr是数组的引用,该数组含有10个指针

  由内向外:首先知道rArr是一个引用;再看右边,可知rArr引用的是一个大小为10的数组;最后看左边,可知数组中的元素是int*(指向int的指针)。

 

数组下标为size_t类型,是一种无符号整数。

两指针相减的结果类型是ptrdiff_t类型(pointer difference
type),是一种带符号整数。

 

对数组a,使用数组名a相当于使用一个指针,其值等于&a[0]。

因此:

int a[10];

int *p = a; //相当于int *p = &a[0]

auto b(a); //相当于auto b(&a[0])

则b的类型是int*,而非int。

但要特别注意:

【特例】当使用decltype关键字时,上述转换不会发生。

int ia[] = {0,1,2};

decltype(ia) ia2 = {4,5,6};
//ia的类型是“由3个整数构成的数组”,故decltype推断ia2结果同样就是“由3个整数构成的数组”(而不是int*)

 

指向数组的指针也是“迭代器”,它支持迭代器的所有运算。(迭代器的运算前面已讨论,包括:解引用、递增/递减、比较大小、与整数相加/相减、两个指针相减等)

 

标准库函数begin和end

这两个函数定义在iterator头文件中(不同于类内的自定义begin和end)。

可以使用这两个函数获取数组的首元素的指针和尾元素的下一位置的指针。

int ia = {0,1,2};

int *be = begin(ia);

int *ed = end(ia);

 

内置的下标运算符所用的索引值不是无符号类型,可以处理负值,这一点与string和vector不一样。

int ia={0,2,4,6,8};

int *p = &ia[2]; //p指向索引为2的值(4)

int i = p[1]; //p[1]等价于*(p+1),也就是ia[3]表示的那个元素(6)

int j = p[-2];
//p[-2]等价于*(p-2),也就是ia[0]表示的那个元素(0)

 

C风格字符串

strlen(p) 返回字符串长度(不包含'')

strcmp(p1, p2) 若p1<p2返回负值

strcat(p1, p2) 将p2附加到p1之后,并返回p1

strcpy(p1, p2) 将p2拷贝给p1,并返回p1

注:p、p1、p2是表示字符串的指针。

以上函数不负责验证其字符串参数。

例如:

char a[] = "abcde", *p = a;

cout<< strlen(p) <<endl; //输出5

cout<< strlen(p+1) <<endl;
//输出4;编译器不会报错,会计算当前指针位置到''位置的距离

cout<< strlen(p+10) <<endl;
//编译器不报错;可能输出不可预料的值或崩溃(strlen会从p+10的位置向前寻找第一个''才停止)

 

混用string对象和C风格字符串

允许用C风格字符串来初始化string对象:

string s("hello");

但不能用string代替C风格字符串:

const char *str = "hello"; //ok

char *str = s; //错误

const char *str = s.c_str(); //ok

注意:c_str()函数返回string对应的C风格字符串,返回类型是const
char*(从而保证了不会通过指针str改变字符数组的内容),因此也必须以const
char*类型的变量接收它。

注意:我们无法保证c_str函数返回的数组一直有效,事实上,如果后续操作改变了s的值,就可能让之前返回的数组失去效用;所以,若执行完c_str后程序想一直正确地使用其返回的数组,最好将该数组拷贝一份。

 

允许用数组来初始化vector对象

int ia[] = {0,1,2,3};

vector<int> iv(begin(ia), end(ia));
//指明数组的首元素地址和尾后地址(尾元素之后一个位置的地址)即可

用于初始化vector的值也可以只是数组中的一部分:

vector<int> iv(a+1, a+3);
//拷贝的下标范围是[1,3),即:将拷贝a[1]、a[2]

 

转载本站文章请注明出处:澳门新葡亰官方网站 http://www.radioritmo-bl.com/?p=1255

上一篇:

下一篇:

相关文章