澳门新葡亰信誉平台游戏C 入门 第八节 指针

by admin on 2020年1月20日

用单引号引起的一个字符实际上代表一个整数,整数值对应于该字符在编译器采用的字符集中的序列值。

void changeValue(int num1,int num2)
{
    int temp = num1;
    num1 = num2;
    num2 = temp;
}
//指针作为函数的参数进行传递,可以实现形参的改变作用到实参

 

用双引号引起的字符串,代表的是一个指向无名数组起始字符的指针,该数组被双引号之间的字符以及一个额外的二进制值为0的字符’’初始化。

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
 

if(flags & FLAG) 是 if(flags & FLAG != 0 ) 的意思,但 if (flags &
FLAG != 0) 是错误的。因为!=运算符的优先级高于&运算符。

//字符指针作为形参
void func2(int *a,int count)
{
    for (int i = 0; i < count; i ++)
    {
        printf(“%d “,*(a+i));
    }
    printf(“n”);
}

初始化

在不进行显示初始化时,外部变量与静态变量都将被初始化为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’, ‘’ };

 

int main(int argc, const char *
argv[]) {

   
/*
    1. 程序运行时,系统会为税局分配内存单元,存储数据.
    2. 内存是由多个连续的内存单元组成
      
内存单元用于存储数据,每个内存单元占一个字节,一个字节==八个二进制位
      
内存单元地址:内存单元的编号,连续的.便于访问内存单元,就像门牌号一样.(图2)
    3. 数据如何存储(图3)
    4. 访问数据的两种方式;
       (1) 直接访问.定义变量存储数据,通过变量名访问数据/
    int a = 10;
    int b = a; //通过变量a访问内存中的数据
    
       (2)
间接访问/通过内存单元地址,访问内存中的数据,任何数值都是存储在内存中的存储单元中.
    5. 内存单元的地址被称为指针
        指针变量: 存储指针(地址)的变量
        定义一个指针变量
    int*p = NULL;
    float *q = NULL;
    char *r = NULL;
    NULL 是空指针;相当于0.NULL是给指针变量赋值的
    定义指针变量的语法
    类型修饰符 *指针变量名 = 初始值(地址)
    * 表示:定义的变量是一个指针变量,存储的内容是地址.
   
类型修饰符表示:地址指向的存储单元中数据的类型(即通过地址找到的数据的类型)
    指针变量的类型:类型修饰符 *    指向某种类型数据的指针类型
    p的类型 int *    指向整型数据的指针类型
    q的类型 float *  指向浮点型数据的指针类型
    r的类型 char *   指向字符型数据的指针类型
    指针变量通常被称为指针
    p 整型指针
    q 浮点型指针
    r 字符型指针
    
    通过指针访问存储单元,必须先获取存储单元的地址
    取地址运算符 & 获取变量的地址
    输出地址 %p
    int a = 100;
    printf(“变量a的存储单元地址:%pn”,&a);
    float b = 1.5;
    printf(“变量b的存储单元地址:%pn”,&b);
    char c = ‘a’;
    printf(“变量c的存储单元地址:%pn”,&c);
    printf(“—————————–n”);
//    定义指针存储地址
//    要求:指针的类型修饰符 和 指针指向的变量的类型必须一致
    int *p = &a;
//   (int *)p = &a;
指针p指向变量a的存储单元(指针p中存储的地址是变量a的存储单元地址)
    
    printf(“指针p中存储的地址:%pn”,p);
    float *q = &b;// *
在定义指针的时候,只起到修饰作用,说明,我定义的是一个指针变量
    printf(“指针q中存储的地址:%pn”,q);
    char *o = &c;
    printf(“指针o中存储的地址:%pn”,o);
    printf(“—————————–n”);
    
//    通过指针访问存储单元
//    访问变量的存储单元的值
    printf(“%dn”,*p); // *p 获取p指向的 内存地址中的值
    
    int i = *p;    // i = a;
    float j = *q;  //j = b;
    char k = *o;   //k = c;
    
    printf(“i = %d j = %.2f k = %cn” ,i ,j ,k);
    
//使用修改数
    a = 200;
    b = 2.5;
    c = ‘b’;
    printf(“a = %d *p = %d n”,a ,*p);

指针

指针是把其他变量地址作为其值的变量。变量通常直接包含一个具体的值,而指针包含的是拥有具体值的变量的地址。变量名直接引用了一个值,而指针是间接地引用了一个值。
澳门新葡亰信誉平台游戏 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* 类型的指针进行比较

r是一个8位整数,且r的低四位与low各位上的数一致,而r的高四位与hi的各位上的数字一致,hi和low是2个整数,值介于0到15之间,则r=(hi
<< 4)+ low;或者r = hi << 4 | low;而r = hi << 4 +
low;是错误的,因为加法的优先级高于移位运算。

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

指针与地址

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

 

// 练习
    int x = 0;
    int y = 0;
    int *m = NULL; //指针m 指向空指针位置
    m = &x; //指针m 重新 指向变量x
    *m = 10; // *m 通过 m  存储的地址访问变量x的存储单元,读取x的值
    printf(“%d “,*m);
    
    m = &y;
    *m = 20;
    printf(“%d “,*m);
    
//指针的存储空间与操作系统有关
//32位操作系统中占4个字节,64位操作系统中占8个字节
    printf(“%lun”,sizeof(m));

指针与数组

通过数组下标所能完成的任何操作都可以通过指针来实现。一般来说,用指针编写的程序比用数组下标编写的程序执行速度快。
 
pa = &a[0] 与 pa =
a是等效的
 
对数组元素a[i]的引用也可以写成*(a+i)这种形式,在计算数组元素a[i]的值时,C语言实际上先将其转换为*(a+i)的形式,然后再进行求值,因此在程序中这两种形式是等价的。另外,&a[i]与 a + i
含义也是相同的。如果pa指向了a数组,则pa[i]与*(pa+1)也是等价的。简而言之,一个通过数组和下标实现的表达式可以等价地通过指针和偏移量实现。
 
但是,数组名和指针之间有一个不同之处,指针是一个变量,因此,pa=a和pa++都是合法的,但数组名不是变量,因此,类似于a = pa,和a++的语句是非法的。请注意,只要是数组类型,就不能改变其指向(除非将它传递到其他函数中进行处理时,因为此是是重新拷贝一份),如 char
*p[],也是不能修改其指向的,如 p++ 是非法的。
 
数组类型的变量虽然是一个指针,但与指针的区别的是一旦声明后就不能再修改它的指向了(因为数组名是一个指向数组起始地址的常量指针),下面程序是错误的:
    char a[1] = “1”;
    char b [2] = “b”
    b=a;
 
char *p,如果确信相应的元素存在,也可以通过下标访问当前指针所指元素前面的元素,可以使用负的下标,如p[-1]、p[-2]等,这也是合法的。
char amessage[] =
“now is the time”;
char *pmessage =
“now is the time”;
amessage始终指向同一个存储位置,而pmessage是一个指针,其初值指向一个字符串常量,之后它可以被修改以指向其他地址。 
澳门新葡亰信誉平台游戏 3
 
如果一个数组的长度是n,&table[-1]和&tab[n]都超出了数组tab的范围,前者在运行过程中肯定是非法的,但是,C语言的定义保证数组末尾之后的第一个元素(即&tab[n])的指针算术运算可以正确执行。
 
数组下标在编译的时候要被转换成指针表示法,所以用指针编写数组下标表达式可节省编译时间。如果 int * p,则p[0]也是可以的:
int main(int argc, char * argv[]) {
    int x = 5;
    int * p = &x;
    printf(“%d %d”, *p, p[0]);//5 5
    return 0;
}
 

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

// 区分指针中的 * 的作用
    1. 如果是在定义指针变量的时候, int *p = NULL; 这个 *
代表的是p是一个指针变量.
    2.
如果定义完成再使用*p的时候,*表示的是取出p指向的地址里面的数据.是取值运算符
 
    指针的运算
    指针只有加减运算
    定义指针时的类型决定指针移到几个字符

指针数组、数组指针

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

移位运算符优先级比算数运算符要低,但是比关系运算符高。

    第一种: p + n
    表示: 从p指向的存储单元向高位偏移n个数据类型的字节数(n *
数据类型的字节数)
    p的指向没有改变

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

几种不同指针的定义

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)   一个数组,其长度未指定,数组的元素为指向函数的指针,该函数没有参数且返回一个整型值
 

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

    int a = 200;
    int *p = &a;
    printf(“%p “,p);
    //printf(“%p “,p ++);
    printf(“%p “,++ p);
*/
/*
     //    1. 定义一个数组
     int a[5] = {1,2,3,4,5};
     //    2. 访问数组元素 数组名[下标]
     a[0] = 10;
     printf(“%dn”,a[0]);
     //    3. 计算数组元素个数
     printf(“%lun”,sizeof(a));
     printf(“%lun”,sizeof(int));
     printf(“%lun”,sizeof(a[0]));
     printf(“%lun”,sizeof(a)/sizeof(int));
     printf(“%lun”,sizeof(a)/sizeof(a[0]));
     //    4. 数组名是常量,表示数组首元素的地址 a = &a[0]
     
     #pragma mark ——–指针与数组————
     
     指针可以指向变量,读取变量存储单元中的值
    
指针可以指向数组元素,数组在内存中是一段连续的存储空间,每个元素都占有相应的存储单元.
     数组元素的指针即数组元素的地址
     
     访问数组元素可以使用数组名或者指向数组的指针
     访问数组元素的两种方式: 下标法 , 指针法
     
     1. 下标法
     
     int a[5] = {1,2,3,4,5};
     int *p = a; //数组名是常量,表示数组首元素的地址 &a[],
此时,p指向数组的第一个元素的存储单元,表示首元素地址.
     
     //   使用数组名
     
     printf(“%d %dn”,a[0],a[1]);
     
     printf(“%d %dn”,p[0],p[1]);
     
     
     
     // 使用指针
     for (int i = 0; i < 4; i ++)
     {
     for (int j = 0; j < 4 – i; j ++)
     {
     if (p[j] < p[j + 1])
     {
     int c = 0;
     c = a[j];
     p[j] = p[j + 1];
     p[j + 1] = c;
     }
     }
     }
     for (int i = 0; i <5; i ++)
     {
     printf(“%d “,p[i]);
     
     }
     
     2. 修改指针指向,不能使用数组名,只能使用指针;
     
     printf(“%d n”,*p); //指向a[0]
     
     // 修改 等价
     int b[5] = {1,2,3,4,5};
     int *q = b;
     b[1] = 20;
     q[1] = 30;
     *(q + 1) = 10;
     *(b + 1) = 20;
     for (int i = 0; i <5; i ++)
     {
     printf(“%d “,b[i]);
     }
     
     指针和数组的区别
     指针和数组都可以通过下标法和指针法访问数组元素
     
     1. 指针可以修改指向
     数组名 是常量,表示首元素的地址,不能改变
     
     2. 指针的存储空间;4/8字节
     数组的存储空间: 元素个数*元素的存储空间
     
     3. sizeof (数组名) 得到的是数组的存储空间
     sizeof
(指针)得到的是4/8字节,不管指针指向的存储空间存储的是哪种数据.
     char a[3] = {‘a’,’b’,’c’};
     char *f = a;
     printf(“%lun”,sizeof(f));
     
     指针类型和数组元素类型不匹配会怎样
     
     short a[4] = {6,7,8};
     int *p = a;
     printf(“%dn”,*p);
*/
#pragma mark ———-指针与字符串———–
/*
    //   
定义字符数组存储字符串,str在栈区中存放,常量字符串拷贝的副本存储在字符数组中,数组中的元素是可以改变的
    char str[] = “hello”;
    char *p = str;
    //  使用指针取修改数组元素
    *p = ‘a’;
    //使用指针放问数组元素
    for (int i = 0; i < 6; i ++)
    {
        printf(“%c “,*(p+i));
    }
    printf(“n”);
    //使用指针操作字符串
    printf(“%sn”,p);
 
    
    定义字符指针指向字符串
    “”常量字符串 , 存储在常量区,只能访问不能修改
    定义指针指向常量区的字符串常量的首地址,指针p中只是存储地址

 

    char *p = “hello”;
 //   *p = ‘a’;
    可以访问字符和字符串
    printf(“%sn”,p);
    printf(“%cn”,*p);

如果两个指针指向的是同一个数组的元素,可以把两个指针相减,这样是有意义的,如:int
*q = p + i;可以通过q -p得到i
的值,但如果q与p指向的不是同一个数组中的元素,即使它们所指向的地址在内存中的位置正好间隔一个数组元素的整数倍,所得到的结果仍然是无法保证其正确性。

//总结:
指向数组的指针可以访问和修改数组元素;指向常量字符串的指针只能访问不能修改.
    
//练习 : 通过指针计算字符串长度
    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”};

 

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

数组名a除了被用作运算符sizeof的参数这一情况,其他所有情形中数组名a都都代表指向数组a中下标为0的元素的指针。*a即数组a中下标为0的元素的引用。*(a+i)即数组a中下标为i的元素的引用,简记为a[i].

    char *strs[3] = {“iOS”,”iPad”,”iMac”};
    printf(“%sn”,*strs);
    printf(“%sn”,*(strs+1));
//   修改
    strs[0] = “Symban”; //strs[0]
访问的是字符指针,指针的重新指向另一个常量字符串
    printf(“%sn”,strs[0]);

 

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

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

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

 

复制指针并不等同与同时复制所指向的数据。

如char *p,*q;   p = “xyz”;

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

 

当常数0被转换为指针使用时,这个指针绝对不能被解除引用。即当我们将0赋值给一个指针变量时,绝对不能企图使用该指针指向内存中存储的内容。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图