小知识汇集
字符串拼接
//C char c[]="abc"; strcat(c,"ba"); //打印c:abcba //C++ string s="abc"; string s=s+"ba"; //打印s:abcba
字符与字符串
//字符 char c[] = 'a'; //字符串 char s[] = "a";//以'\0'结束
char s[2]={'a','b','\0'};//必须加这\0字符串结束标志
const
int a= 3,*p;//a所在内存单元(地址)为100 p = &a; //相当于p里面存放了a的地址,p的值是a的地址(p=&a=100) *p = a; //值是取a的地址100这个内存单元中存放的值(*p=a=3) const int* p; //p可变,p指向的内容不可变 int const* p; //p可变,p指向的内容不可变 int* const p; //p不可变,p指向的内容可变 const int* const p; //p和p指向的内容都不可变
口诀:左数据右指针 为常量
当const出现在*号左边时指针指向的数据为常量
当const出现在*号右边时指针本身为常量,只能指向这一个地址
内存分布
int a = 0; //全局初始化区, 可以被其他c文件 extern 引用 static int ss = 0; //静态变量,只允许在本文件使用 char *p1; //全局未初始化区 void main(void) { int b; //栈 char s[] = "abc"; //栈 char *p2; //栈 char *p3 = "123456"; //123456\0在常量区, p3在栈上。 static int c =0; //全局(静态)初始化区 p1 = (char *)malloc(10); //在堆区申请了10个字节空间 p2 = (char *)malloc(20); //在堆区申请了20个字节空间 strcpy(p1, "123456"); /* 123456字符串(结束符号是0,总长度7)放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方 */ }
*p++与(*p)++
*p++是指下一个地址。
(*p)++是指将*p所指的数据的值加一。
C编译器认为*和++是同优先级操作符,且都是从右至左结合的,
所以*p++中的++只作用在p上,和*(p++)意思一样;在(*p)++中,
由于()的优先级比*和++都高,
所以++作用在()内的表达式*p上。
比如有:
int x,y,a[]={1,2,3,4,5},*p=a,*q=a; x=*p++;//执行后x=a[0]=1,p=a+1 y=(*q)++;//执行后,y=a[0]+1=2,q仍然=a
函数参数
void xchange1(int *n1,int *n2);//指针形参 void xchange2(int &n1,int &n2);//引用形参 int main(void) { int num1 = 9,num2 = 0; cout<<num1<<' '<<num2<<endl; xchange1(&num1,&num2); cout<<num1<<' '<<num2<<endl; xchange2(num1,num2); cout<<num1<<' '<<num2<<endl; system("pause"); return 0; } void xchange1(int *n1,int *n2) { auto temp = *n1; *n1 = *n2; *n2 =temp; } void xchange2(int &n1,int &n2) { auto temp = n1; n1 = n2; n2 = temp; }
递归
//形如: void function() { function(); /* function 本身 */ } int main() { function(); } //示例 /*将字符串循环右移n个单位*/ void move(char s[], int n) { if( n == 0) return ; else{ int len = strlen(s); int temp = s[len-1]; int i; for( i = len-1; i > 0 ; --i) { s[i] = s[i-1]; } s[i] = temp; move(s, n-1); } }
递归:一层层的进入就会一层层的退出,若遇到出口直接退出
多维数组与指针
一维数组与数组指针
假如有一维数组如下:
char a[3];
一个数组的数组名代表其首元素的首地址,也就是相当于&a[0],而a[0]的类型为char,因此&a[0]类型为char *,因此,可以定义如下的指针变量:
char * p = a;//相当于char * p = &a[0]
以上文字可用如下内存模型图表示。
a和&a[0]代表的都是数组首元素的首地址,而如果你将&a的值打印出来,会发现该值也等于数组首元素的首地址。请注意我这里的措辞,也就是说,&a虽然在数值上也等于数组首元素首地址的值,但是其类型并不是数组首元素首地址类型,也就是char *p = &a是错误的。
对数组名进行取地址操作,其类型为整个数组,因此,&a的类型是char (*)[3],所以正确的赋值方式如下:
char (*p)[3] = &a;
注:很多人对类似于a+1,&a+1,&a[0]+1,sizeof(a),sizeof(&a)等感到迷惑,其实只要搞清楚指针的类型就可以迎刃而解。比如在面对a+1和&a+1的区别时,由于a表示数组首元素首地址,其类型为char *,因此a+1相当于数组首地址值+sizeof(char);而&a的类型为char (*)[3],代表整个数组,因此&a+1相当于数组首地址值+sizeof(a)。(sizeof(a)代表整个数组大小,但是无论数组大小如何,sizeof(&a)永远等于一个指针变量占用空间的大小)
二维数组与数组指针
假如有如下二维数组:
char a[3][2];
由于实际上并不存在多维数组,因此,可以将a[3][2]看成是一个具有3个元素的一维数组,只是这三个元素分别又是一个一维数组。实际上,在内存中,该数组的确是按照一维数组的形式存储的,存储顺序为(低地址在前):a[0][0]、a[0][1]、a[1][0]、a[1][1]、a[2][0]、a[2][1]。(此种方式也不是绝对,也有按列优先存储的模式)
为了方便理解,我画了一张逻辑上的内存图,之所以说是逻辑上的,是因为该图只是便于理解,并不是数组在内存中实际的存储模型(实际模型为前文所述)。
如上图所示,我们可以将数组分成两个维度来看,首先是第一维,将a[3][2]看成一个具有三个元素的一维数组,元素分别为:a[0]、a[1]、a[2],其中,a[0]、a[1]、a[2]又分别是一个具有两个元素的一维数组(元素类型为char)。从第二个维度看,此处可以将a[0]、a[1]、a[2]看成自己代表”第二维”数组的数组名,以a[0]为例,a[0](数组名)代表的一维数组是一个具有两个char类型元素的数组,而a[0]是这个数组的数组名(代表数组首元素首地址),因此a[0]类型为char *,同理a[1]和a[2]类型都是char *。而a是第一维数组的数组名,代表首元素首地址,而首元素是一个具有两个char类型元素的一维数组,因此a就是一个指向具有两个char类型元素数组的数组指针,也就是char(*)[2]。
也就是说,如下的赋值是正确的:
char (*p)[2] = a;//a为第一维数组的数组名,类型为char (*)[2] char * p = a[0];//a[0]维第二维数组的数组名,类型为char *
同样,对a取地址操作代表整个数组的首地址,类型为数组类型(请允许我暂且这么称呼),也就是char (*)[3][2],所以如下赋值是正确的:
char (*p)[3][2] = &a;
三维数组与数组指针
假设有三维数组:
char a[3][2][2];
同样,为了便于理解,特意画了如下的逻辑内存图。分析方法和二维数组类似,首先,从第一维角度看过去,a[3][2][2]是一个具有三个元素a[0]、a[1]、a[2]的一维数组,只是这三个元素分别又是一个"二维"数组,a作为第一维数组的数组名,代表数组首元素的首地址,也就是一个指向一个二维数组的数组指针,其类型为char (*)[2][2]。从第二维角度看过去,a[0]、a[1]、a[2]分别是第二维数组的数组名,代表第二维数组的首元素的首地址,也就是一个指向一维数组的数组指针,类型为char(*)[2];同理,从第三维角度看过去,a[0][0]、a[0][1]、a[1][0]、a[1][1]、a[2][0]、a[2][1]又分别是第三维数组的数组名,代表第三维数组的首元素的首地址,也就是一个指向char类型的指针,类型为char *。
由上可知,以下的赋值是正确的:
char (*p)[3][2][2] = &a;//对数组名取地址类型为整个数组 char (*p)[2][2] = a; char (*p) [2] = a[0];//或者a[1]、a[2] char *p = a[0][0];//或者a[0][1]、a[1][0]...
多级指针
所谓的多级指针,就是一个指向指针的指针,比如:
char *p = "my name is chenyang."; char **pp = &p;//二级指针 char ***ppp = &pp;//三级指针
假设以上语句都位于函数体内,则可以使用下面的简化图来表达多级指针之间的指向关系。
//n级指针变量名Pn等于取n-1级指针变量名Pn-1,int ***Pn,**Pn-1;Pn=&(Pn-1); #include <stdio.h> int main (){ int a = 2; int *p,**pp,***ppp; p=&a; pp=&p; ppp=&pp; printf("p = %d\npp = %d\nppp = %d\n",*p, **pp, ***ppp); }
多级指针通常用来作为函数的形参,比如常见的main函数声明如下:
int main(int argc,char ** argv)
因为当数组用作函数的形参的时候,会退化为指针来处理,所以上面的形式和下面是一样的。
int mian(int argc,char* argv[])
argv用于接收用户输入的命令参数,这些参数会以字符串数组的形式传入,类似于:
char * parm[] = {"parm1","parm2","parm3","parm4"};//模拟用户传入的参数 main(sizeof(parm)/sizeof(char *),parm);//模拟调用main函数,实际中main函数是由入口函数调用的(glibc中的入口函数默认为_start)
多级指针的另一种常见用法是,假设用户想调用一个函数分配一段内存,那么分配的内存地址可以有两种方式拿到:第一种是通过函数的返回值,该种方式的函数声明如下:
void * get_memery(int size) { void *p = malloc(size); return p; }
第二种获取地址的方法是使用二级指针,代码如下:
int get_memery(int** buf,int size) { *buf = (int *)malloc(size); if(*buf == NULL) return -1; else return 0; } int *p = NULL; get_memery(&p,10);
数组指针与指针数组
首先,理解一下数组指针和指针数组这两个名词:
“数组指针”和“指针数组”,只要在名词中间加上“的”字,就知道中心了——
数组的指针:是一个指针,什么样的指针呢?指向数组的指针。
指针的数组:是一个数组,什么样的数组呢?装着指针的数组。
然后,需要明确一个优先级顺序:()>[]>*,所以:
(*p)[n]:根据优先级,先看括号内,则p是一个指针,这个指针指向一个一维数组,数组长度为n,这是“数组的指针”,即数组指针;
*p[n]:根据优先级,先看[],则p是一个数组,再结合*,这个数组的元素是指针类型,共n个元素,这是“指针的数组”,即指针数组。
根据上面两个分析,可以看出,p是什么,则词组的中心词就是什么,即数组“指针”和指针“数组”。
数组指针 (*p)[n]
数组指针:是指针——指向数组的指针。
看下面的例子进行理解:
#include "stdafx.h" int main() { //一维数组 int a[5] = { 1, 2, 3, 4, 5 }; int(*p)[5]; p = &a;//把数组a的地址赋给p,则p为数组a的地址,则*p表示数组a本身 printf("%p\n", a); //输出数组a的地址,一般用数组的首元素地址来标识一个数组 printf("%p\n", p); //根据上面,p为数组a的地址,输出数组a的地址 printf("%p\n", *p); //*p表示数组a本身,一般用数组的首元素地址来标识一个数组 printf("%p\n", &a[0]); //a[0]的地址 printf("%p\n", &a[1]); //a[1]的地址 printf("%p\n", p[0]); //数组首元素的地址 printf("%d\n", **p); //*p表示地址,则*(*p)表示值,当*p表示数组首元素地址时,**p表示首元素本身,即首元素的值1 printf("%d\n", *p[0]); //根据优先级,p[0] 表示首元素地址,则*p[0]表示首元素本身,即首元素的值1 printf("%d\n", *p[1]); //错误,不表示a[1]...表示什么我还不知道 //将二维数组赋给指针 int b[3][4]; int(*pp)[4]; //定义一个数组指针,指向含4个元素的一维数组 pp = b; //将该二维数组的首地址赋给pp,也就是b[0]或&b[0],二维数组中pp=b和pp=&b[0]是等价的 pp++; //pp=pp+1,该语句执行过后pp的指向从行b[0][]变为了行b[1][],pp=&b[1] int k; scanf_s("%d", &k); return 0; }
指针数组 *p[n]
指针数组:是数组——装着指针的数组。
看下面的例子进行理解:
#include "stdafx.h" int main() { int a = 1; int b = 2; int *p[2]; p[0] = &a; p[1] = &b; printf("%p\n", p[0]); //a的地址 printf("%p\n", &a); //a的地址 printf("%p\n", p[1]); //b的地址 printf("%p\n", &b); //b的地址 printf("%d\n", *p[0]); //p[0]表示a的地址,则*p[0]表示a的值 printf("%d\n", *p[1]); //p[1]表示b的地址,则*p[1]表示b的值 //将二维数组赋给指针数组 int *pp[3]; //一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2],所以要分别赋值 int c[3][4]; for (int i = 0; i<3; i++) pp[i] = c[i]; int k; scanf_s("%d", &k); return 0; }
最后,从上文来看:
数组指针是一个指针变量,占有内存中一个指针的存储空间;
指针数组是多个指针变量,以数组的形式存储在内存中,占有多个指针的存储空间。
指向二维数组,取值:
#include "stdafx.h" int main() { /*同时指向二维数组时,引用、用数组名引用表达是相同的 定义一个2行3列的二维整型数组 0 1 2 3 4 5 输出第1行第2列的值:5*/ int a[2][3] = { {0, 1, 2}, {3, 4, 5} }; //2行3列的二维整型数组 int(*p)[3]; //数组指针,指向含有3个元素的一维数组 int *q[2]; //指针数组,一个数组内存放2个指针变量 p = a; q[0] = a[0]; q[1] = a[1]; //输出第1行第2列的值 printf("%d\n", a[1][2]); //5 printf("%d\n", *(p[1] + 2)); //5 printf("%d\n", *(*(p + 1) + 2)); //5 printf("%d\n", (*(p + 1))[2]); //5 printf("%d\n", p[1][2]); //5 printf("%d\n", *(q[1] + 2)); //5 printf("%d\n", *(*(q + 1) + 2)); //5 printf("%d\n", (*(q + 1))[2]); //5 printf("%d\n", q[1][2]); //5 int k; scanf_s("%d", &k); return 0; }