Joker

导航

小知识汇集

字符串拼接

//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;
}

 

 

typedef

posted on 2018-09-25 16:32  Joker·GS  阅读(131)  评论(0编辑  收藏  举报