C语言回顾(六、基类型,数组指针,字符串指针,函数指针,文字常量区)——iOS开发基础

时间:2021-04-18 00:26:47

说明:
在学习UI高级知识之前,将利用最近十来天的时间回顾一下C语言,主要按照《C程序设计(谭浩强版)》来回顾。
整理一些知识点(不是细节,知识个人觉得较重要或易忘的)以及挑一些课后题目或经典习题编写代码练习。


第8章 善于利用指针


1、“指针”和“指针变量”概念区分
指针:地址,指向以它为地址的内存单元
指针变量:存放地址的变量
(1)&——取地址符,&a是变量a的地址
(2)* ——指针运算符(间接访问符),*p代表指针变量p指向的对象

2、“基类型”

    int* p1;    //p1是指向int型变量的指针变量,简称int指针
    float* p2;  //p2是指向float型变量的指针变量,简称float指针

int,float就是“基类型”
定义变量时必须指定基类型
(1)因为不同类型的数据在内存中所占的字节数和存放方式是不同的,知道该数据类型,才能按存储单元的长度以及数据的存储形式正确地取出该数据。同样影响指针的移动和运算。
(2)一个变量的指针含义两方面:1、存储单元编号表示的地址;2、指向的存储单元的数据类型。

3、指针运算
type *p = addr;
p+i —> addr+i*sizeof(type);
补充:如果p1,p2都指向同一数组,结果是p2-p1的值(两个地址之差)除以数组元素的长度,该值表示两个元素的相对距离(差几个元素)。

4、指针与数组
(1)数组名的含义

#include <stdio.h>

int main(int argc, const char * argv[]) {
    int a[10] = {0};
    int *p;
    p = a;  //数组名代表首元素的地址 &a[0]
    printf("%lu\n",sizeof(a));  //数组名代表整个数组
    printf("%p,%p",p,&a[0]);
    return 0;
}

output:

40
0x7fff5fbff7d0,0x7fff5fbff7d0

(2)公式
*(p+i) = p[i] =
*(a+i) = a[i]
注意:不建议使用p[i]。因为只有p指向a[0]时,p[i]代表a[i],如果p不是指向a[0]那么非常容易出错。
(3)多维数组
int a[3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}};
(1)a是首行(序列为0)的首地址,a+1代表序列为1的行的首地址。
(2)a[0],a[1]既是一维数组名,又代表数组首元素地址&a[0][0],&a[1][0]
(3)*(a[i]+j),*(*(a+i)+j)是二维元素a[i][j]的值
a[1]+2 = *(a+1)+2 = &a[1][2]
(将a[1]视为一个整体,千万不要写成*(a+1+2),这样等价于a[3]了)

#include <stdio.h>

int main(int argc, const char * argv[]) {
    int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
    int (*p)[4],i,j;    //(*p)[4]指向包含4个整型元素的一维数组(*p)括号不可少
    p = a;              //p指向二维数组的第0printf("Please input row:");
    scanf("%d",&i);
    printf("Please input colum:");
    scanf("%d",&j);
    printf("a[%d][%d] = %d\n",i,j,*(*(p+i)+j));
    return 0;
}

output:

Please input row:0
Please input colum:0
a[0][0] = 1
(*p)[4]的理解:(*p)是一个数组,有4个元素。p指向有4个元素的数组,是指向一维数组的指针,其值是该一维数组的起始地址。

理解起来较困难,好好体会。

5、字符串与指针
(1)将字符串的第1个字符的地址赋给指针变量
通过程序验证 以下的区别
char *p = “hello”;
char a[] = “hello”

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
    char *p = "hello";
    char a[] = "hello";
    int i;
    puts(a);
    for(i=0;a[i]!='\0';i++)
    {
        a[i]=i;
    }
    printf("change_a=");
    for(i=0;i<5;i++)
    {
        printf("%d",a[i]);
    }
    printf("\n");
//此处以下 
    puts(p);
    for(i=0;p[i]!='\0';i++)
    {
        p[i]=i;
    }
    printf("change_p=");
    for(i=0;p[i]!='\0';i++)
    {
        printf("%d",p[i]);
    }
            // 语法上没有错误,但是运行到此不停止工作
    return 0;
}

(2) 文字常量区 :只能读不能写
char *p = “hello”; 字符串存储在文字常量区,p记录这块内存第一个字节的地址
char a[] = “hello”; 字符串存储在数组中
因此上述程序运行结果,无法输出修改后的结果,因为存在文字常量区,只能读不能写。
output:

hello change_a=01234 hello (lldb) 

(3)字符指针做函数参数,下列函数有很多赋值指针的不同形式写法,没有用到strcpy。

#include <stdio.h>

void copy_string1(char from[],char to[]) {  //形参为字符数组
    int i = 0;
    while (from[i] != '\0') {
        to[i] = from[i];
        i++;
    }
    to[i] = '\0';
}

void copy_string2(char* from,char* to) {    //形参为字符指针变量
    for (; *from != '\0'; from++,to++) {
        *to = *from;
    }
    *to = '\0';
}

//程序改进!
void copy_string3(char* from,char* to) {    //形参为字符指针变量
    while ((*to = *from) != '\0') {
        to++;
        from++;
    }
//OR
// while ((*to++ = *from++) != '\0');
//OR
// while ((*to++ = *from++));
//OR
// for (;(*to++ = *from++) != '\0';);
//OR
// for (;(*to++ = *from++););
}

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

    char a[] = "China";
    char b[] = "Zhejiang";
    char* from = a;
    char* to = b;

    printf("Before:%s,%s\n",a,b);
    copy_string3(from, to);
    printf("After :%s,%s\n",a,b);

    return 0;
}

output:
不管调用哪种方法,结果都是一样的。

Before:China,Zhejiang
After :China,China

(3)可变格式输出函数

    char *format = "a = %d,b = %d\n";
    printf(format,a,b);
    //等价于
    printf("a = %d,b = %d\n",a,b);

//也可以用字符数组实现
    char format[] = "a = %d,b = %d\n";
    printf(format,a,b);
//记住字符数组如果不是初始化,就要逐个元素赋值!!!
    //所以下列方法是不可理的!!!
    //char format[];
    //format = "a = %d,b = %d\n"

这个类似于Objective-C中得stringWithFormat。

6、函数指针:指向函数的指针变量
(1)类型名 (*指针变量名)(函数参数表列)
类型名为函数返回值类型,上述括号缺一不可!不理解参考C程序设计第4版268页说明。
通过一个简单程序来说明其使用:

#include <stdio.h>

int main(int argc, const char * argv[]) {
    int max(int x,int y);   //函数声明
    int a = 3,b = 5,c;
    int (*p)(int,int);      //定义指向函数的指针变量p
    p = max;                //使p指向max函数,只需给出函数名,不需要参数
    //如果写成 p = max(a,b) 那么是将调用max函数所得到的函数值赋给p了,而不是将函数的入口地址给p
    c = (*p)(a,b);
    printf("%d,%d max:%d\n",a,b,c);

    return 0;
}

int max(int x,int y) {
    return x>y?x:y;
}

output:3,5 max:5
(2)函数指针做函数参数,道理相同

void func(int (*x1)(int,int),int (*x2)(int,int)) {
    …………
}

有点嵌套调用的意思func(min,max);
差不多类似(*x1) = min; (*x2) = max;的操作