C语言---数组难点梳理

目录

概述:

1. 一维数组代码demo:

2. 二维数组代码demo:

 3. 字符数组代码demo:

4. 数值型数组作为函数实参传递

5. 字符数组/指针作为函数实参传递

6. 数组作为函数返回值

概述:

C语言的语法不能靠死记硬背,而是需要理解,真正理解了背后的原理,C数组的难点也就不攻自破了。特别需要重点深入理解甚至死记的知识点如下:

  1. 计算机内存被分成一个个的内存单元,每个单元大小为一个字节,给每个单元一个编号就是这个单元的地址,地址也被称为指针。所以是否可以重新赋值就涉及到地址是否可以重新赋值,还有就是地址指向的值是否可以重新赋值。
  2. 数组占有连续的内存单元,所以非常适合指针操作(copy等操作效率更高),所以数组名会被编译器自动隐式的转换成常量指针(有两个例外:对数组名取地址时;sizeof求数组内存大小时),所以数组名只能加一指向下一个元素,但是不能把下一个元素的地址赋值给数组名,所以数组名永远指向第一个元素的地址。
  3. 字符串在C语言中是常量,所以C语言是没有字符串类型的,而是使用字符数组或字符指针来描述字符串常量。
  4. 字符数组里保存的是字符串常量,所以值不能重新赋,但是数组元素是单个字符变量,所以可以重新赋值,即可以针对单个元素重新赋值。
  5. 字符指针保存的是字符串常量的地址,字符串常量本身被保存在常量区。所以,指针是可以重新赋值指向新的地址的。但是,指针指向的值是字符串常量,所以指向的值不可以重新赋成其他字符串。但是如果指针指向的是字符数组,则可以通过strcpy来重新赋值。
  6. sizeof(变量名):返回的是类型占用内存的大小,所以sizeof的参数可以是各种类型比如int,所以该值得大小在编译时就已经确定了。
  7. strlen(字符指针名/字符数组名):返回字符串中真正字符的个数。不包括结尾字符。所以该值得大小必须到运行时才能确定。

 

 

1. 一维数组代码demo:

/*
 * author: tianqiang
 * date : 2022/05/12
 * description: 1)+1”就是偏移量问题:一个类型为T的指针的移动,是以sizeof(T)为移动单位。所以首先要明确指针类型。
 *              2) 数组名表示数组首元素的地址, 数组名 + 1 表示第2个元素的地址;
 *              3)对数组名取地址表示整个数组的地址,数组名取地址后再 + 1,表示尾指针,即紧跟数组最后一个元素的地址;
 *              4)形象对比:中国政府在北京市,北京市的政府也在北京市,但是两者显然是不同的;
 *              5)也可以认为,数组名表示的是一维数组,对数组名取地址可以认为是二维数组;
 *              6) 数组名,&数组名[0], &数组名 三者值相同,但是第三个与前两个含义不同
 *              7)数组名 + 1, &数组名[0] +1,值和意义都相同:第二个元素的地址
 */

#include <stdio.h>

int main () {
    int scoreArray[10] = {99, 89, 78, 90};
    printf("scoreArray size = %d \n", sizeof(scoreArray)); //数组占用的内存单元字节数:40
    printf("scoreArray element = %d \n", sizeof(scoreArray)/sizeof(int)); //数组分配的元素个数:10
    printf("scoreArray = %p, *scoreArray = %d \n", scoreArray, *scoreArray); //数组名是数组首元素的地址
    printf("scoreArray + 1 = %p, *(scoreArray + 1) = %d \n", scoreArray + 1, *(scoreArray + 1)); //数组名+1为第二个元素的地址
    printf("&scoreArray = %p, *(&scoreArray) = %p \n", &scoreArray, *(&scoreArray)); //数组名取地址,是整个数组的地址
    printf("&scoreArray + 1 = %p, *(&scoreArray + 1) = %d \n", &scoreArray + 1, *(&scoreArray + 1)); //数组名取地址后再+1是尾指针
    printf("&scoreArray[0] = %p \n", &scoreArray[0]);
    printf("&scoreArray[0] + 1 = %p, *(&scoreArray[0] + 1) = %d \n", &scoreArray[0] + 1, *(&scoreArray[0] + 1)); //完全等同于数组名

    return 0;
}
/* out put:
 * scoreArray size = 40 //编译器分配内存的总字节数。
 * scoreArray element = 10 //编译器分配总元素的个数,并不是实际元素的个数
 * scoreArray = 0x7fff030bb710, *scoreArray = 99 //数组名表示首元素的地址 类型:int *
 * scoreArray + 1 = 0x7fff030bb714, *(scoreArray + 1) = 89 //数组名 + 1:指向第二个元素
 * &scoreArray = 0x7fff030bb710, *(&scoreArray) = 0x7fff030bb710 //&数组名:数组的地址,类型:int (*)[10]
 * &scoreArray + 1 = 0x7fff030bb738, *(&scoreArray + 1) = -51099448 //&数组名+1:最后一个元素后面的地址
 * &scoreArray[0] = 0x7fff030bb710
 * &scoreArray[0] + 1 = 0x7fff030bb714, *(&scoreArray[0] + 1) = 89 //首元素地址+1:表示第二个元素的地址
 */

2. 二维数组代码demo:

/*
 * author: tianqiang
 * date : 2022/0512
 * description: 1) 二维数组可以看成一个一维数组,其元素又是一个一维数组;
 *              2)二维数组名表示第0行元素(一维数组)的地址,arr + i表示第i行元素(一维数组)的地址;
 *              2)所以后面的内容就可以过渡到一维数组知识里
 *              3)数组名就是指向数组第一个元素的指针,而其类型就是第一个元素的类型.
 *              4)T类型的指针移动,以sizeof(T)为移动单位
 *              5) 二维数组名aa, aa[0],*aa,&aa值相等,但是意义不同。
 */

#include <stdio.h>

int main () {
    int array2[2][3] = {{1,2,3}, {44, 55, 66}};
    printf("array2 = %p, *array2 = %p, **array2 = %d \n", array2, *array2, **array2);
    printf("arrary2 + 1 = %p, *(array2 +1) = %p, **(array2 + 1) = %d \n", array2 + 1, *(array2 + 1), **(array2 + 1));

    printf("array2[0] = %p, *array2[0] = %d, array2[0][0] = %d \n", array2[0], *array2[0], array2[0][0]);

    return 0;
}
/*
 * out put:
 * array2 = 0x7ffe260e2b90, *array2 = 0x7ffe260e2b90, **array2 = 1 //二维数组名:第0行元素(一维数组)的地址
 * arrary2 + 1 = 0x7ffe260e2b9c, *(array2 +1) = 0x7ffe260e2b9c, **(array2 + 1) = 44 //二维数组名 + 1:第1行元素(一维数组)的地址
 * array2[0] = 0x7ffe260e2b90, *array2[0] = 1, array2[0][0] = 1
 *
 */

 3. 字符数组代码demo:

/*
 * author: tianqiang
 * date:   2022/05/13
 * description: 1) 字符数组名是指针常量指向数组首元素,可以+1,但是不能重新赋值
 *              2)字符指针名指向首字符,可以+1,也可以赋值,所以,字符指针名具体指向要根据上下文来定
 *              3)sizeof返回编译器针对该类型分配的内存大小
 *              4)strlen返回字符的实际个数,不包括'\0
 *              5)%s输出字符串,需要提供地址,而不是值
 *              6)%c输出字符,需要的是指,而不是地址
 *              7)字符数组:字符串直接保存在字符数组的内存单元里;
 *              8)字符指针:字符串被保存在常量区,字符指针保存了字符串的地址
 */

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

int main () {
    char cArray[] = "tianQiang";
    char cArrayError[35] = "qqqqq";
    char *cPointer = cArray;
    char *cPointerP = "tianQiang";

    printf("cArray = %s, sizeof(cArray) = %d, strlen(cArray) = %d \n", cArray, sizeof(cArray), strlen(cArray));
    printf("cArrayError = %s, strlen(cArrayError) = %d, sizeof(cArrayError) =%d  \n", cArrayError, strlen(cArrayError), sizeof(cArrayError));

    strcpy(cArrayError, cArray);
    printf("cArray = %s, cArrayError = %s \n", cArray, cArrayError);
    printf("strlen(cPointer) = %d, cPointer = %s, *cPointer = %c \n", strlen(cPointer), cPointer, *cPointer);
    printf("strlen(cPointerP) = %d, cPointerP = %s, *cPointerP = %c \n", strlen(cPointerP), cPointerP, *cPointerP);

    printf("cPointerP = ");
    while(*cPointerP) {
        printf(" %c", *(cPointerP++)); //字符指针可以重新赋值
    }
    printf("\n");
   // cArray++; //编译报错:eror: lvalue required as increment operand

    return 0;
}
/*
 * out put
 * cArray = tianQiang, sizeof(cArray) = 10, strlen(cArray) = 9            //sizeof返回编译器针对该类型分配的内存大小
 * cArrayError = qqqqq, strlen(cArrayError) = 5, sizeof(cArrayError) =35  //strlen返回字符的实际个数,不包括'\0'
 * cArray = tianQiang, cArrayError = tianQiang
 * strlen(cPointer) = 9, cPointer = tianQiang, *cPointer = t              //%s输出字符串,需要提供地址,而不是值
 * strlen(cPointerP) = 9, cPointerP = tianQiang, *cPointerP = t           //%c输出字符,需要的是指,而不是地址
 * cPointerP =  t i a n Q i a n g
 */

4. 数值型数组作为函数实参传递

 * author: tianqiang
 * date  : 2022/0515
 * description: 数值型数组名作为参数传递给函数
 *             1)  数组名被自动转换成首元素的地址;
 *             2) 遵守值传递,所以首元素的地址不会被改变;
 *             3) 但是由于实行参共用同一块内存地址,所以值会同时改变。
 */

#include <stdio.h>
const int PASS_SCORE = 60;
void printMathScore(const float *p, int n); //参数为指针类型,需要提供第二个参数提供数组的大小。
void changeScore(float *p, int n);
//void changeScore(float score[], int n); //最终还是会转换成指针

int main () {
    float mathScore[6] = {98, 37, 56, 61, 50, 49};
    printMathScore(mathScore, sizeof(mathScore)/sizeof(float)); //调用时,数组大小一般是计算得到。

    changeScore(mathScore, sizeof(mathScore)/sizeof(float));
    printMathScore(mathScore, sizeof(mathScore)/sizeof(float));

    return 0;
}

void changeScore(float *p, int n) {
    for(int i = 0; i < n; i++) {
        if ((*(p + i) < PASS_SCORE) && (PASS_SCORE - *(p + i) <= 10)) {
            *(p + i) = PASS_SCORE;
        }
    }
}

void printMathScore(const float *p, int n) { //如果不允许数组值被改变,就需要使用const关键字。
    printf("mathScore = ");
    for (int i = 0; i < n; i++) {
        printf("%f \t", *(p + i));
    }
    printf("\n");
}
/*
 * out put: 可以看到实参和形参的值都被改变了,但是一定要理解,首元素的地址没有改变,所以还是值传递的。
 * mathScore = 98.000000   37.000000       56.000000       61.000000       50.000000       49.000000
 * mathScore = 98.000000   37.000000       60.000000       61.000000       60.000000       49.000000
 */

5. 字符数组/指针作为函数实参传递

/*
 * author: tianqiang
 * date  :2022/05/15
 * description: 字符数组/字符指针作为函数实参传递
 *           1)字符指针由于有结尾标志,所以不需要传递size参数
 *           2)温故知新:字符数组仅仅可以针对字符元素重新赋值;数组名对应的指针可以+1,
 *                        但是不能重新赋值,所以数组名永远指向首元素
 *           3)温故知新:字符指针指向的是字符串常量,所以不能重新赋值。
 *                        但是字符指针本身可以重新赋值,指向其他地址的。
 *           4)温故知新:字符指针指向字符数组后,就可以通过strcpy函数进行整体赋值
 */

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

void changeName(char *name);
void changeCharArrayName(char *name);
int main () {
    char ch[] = "tianqiang"; //字符数组仅仅可以针对字符元素重新赋值
    ch[0] = 'd';
    //char *nameP = "tianqiang"; //字符指针是常量,不能重新赋值;
    char *nameP = ch; //字符指针指向字符数组后,就可以通过strcpy函数进行整体赋值。

    printf("nameP = %s \n", nameP);
    changeName(nameP);
    printf("after, nameP = %s \n", nameP);

    ch[0] = 'd';
    ch[1] = 'd';
    ch[2] = 'd';
    printf("ch = %s \n", ch);
    changeCharArrayName(ch);
    printf("after, ch = %s \n", ch);
}

void changeName(char *name) { //指向字符数组的指针可以用strcpy函数来重新赋值
    strcpy(name, "jian");
}

void changeCharArrayName(char *name) { //char数组本身只能单个元素重新赋值
    for (int i =0; i < strlen(name); i++) {
        name[i] = 'm';
    }
}
/*
 * out put:
 * nameP = dianqiang
 * after, nameP = jian
 * ch = dddn
 * after, ch = mmmm
 */

6. 数组作为函数返回值

 /*
  2  * author: qianqiang
  3  * date:   2022/0517
  4  * description: 数组作为函数返回值
  5  *              1)明确不能直接返回数组,而是返回该类型的指针
  6  *              2)字符指针常量不能重新赋值,所以先指向字符数组,才可以重新赋值
  7  */
  8
  9 #include <stdio.h>
 10 #include <string.h>
 11
 12 int * returnScore(int *array, int size);
 13 void printScore(int *p, int size);
 14 char * returnName(char *p);
 15
 16 int main () {
 17     int score[] = {88, 99, 56};
 18     int *pScore = score;
 19     char name[] = "tianqiangqi";
 20     char *pName = name;
 21     pScore = returnScore(score, sizeof(score)/sizeof(int));
 22     printf("score = %p, pScore = %p \n", score, pScore);
 23
 24     printf("pScore = ");
 25     printScore(pScore, sizeof(score)/sizeof(int));
 26     printf("score = ");
 27     printScore(score, sizeof(score)/sizeof(int));
 28     printf("22222 score = %p, pScore = %p \n", score, pScore);
 29
 30     printf("pName = %s, pName addr = %p \n", pName, pName);
 31     pName = returnName(name);
 32     printf("pName = %s, pName addr = %p \n", pName, pName);
 33
 34 }
 35
 36 int * returnScore(int *p, int size) {
 37     for(int i = 0; i < size; i++) {
 38         *(p + i) = *(p+i) - 50;
 39     }
 40     return p;
 41 }
 42
 43 char * returnName(char *p) {
 44     strcpy(p, "jianmeiLiu");
 45     return p;
 46 }
 47
 48 void printScore(int *p, int size) {
 49     for(int i = 0; i < size; i++) {
 50         printf("%d \t", *(p + i));
 51     }
 52     printf("\n");
 53 }
 54 /*
 55  * out put:
 56  * score = 0x7ffeedc736a0, pScore = 0x7ffeedc736a0
 57  * pScore = 38     49      6
 58  * score = 38      49      6
 59  * 22222 score = 0x7ffeedc736a0, pScore = 0x7ffeedc736a0
 60  * pName = tianqiangqi, pName addr = 0x7ffeedc736ac
 61  * pName = jianmeiLiu, pName addr = 0x7ffeedc736ac
 62  *
 63  */

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值