上一篇简单说了一下数组的基本概念以及在内存中的应用,这一篇将详细解析数组和指针数组还有函数指针数组这三者的关系
一丶 数组的进阶
1. 数组名的含义
- 在C语言程序中,数组的出现又两种可能含义:
- 代表整个数组
- 代表其首元素的地址
- 当出现以下情形时,数组代表的是整个数组
- 在数组定义中
- 在sizeof运算符表达式中
- 在取地址符
- 当出现其他情形时,数组代表其首元素的地址
- 指针指向该数组首元素的地址时
- 函数传参时
- 使用scanf函数
- 直接使用数组名
- 取址的时候&a,用sizeof求数组大小的时候sizeof(a) = 16,数组名代表整个数组
#include <stdio.h>
// 主函数
int main(int argc, char const *argv[])
{
// (1)、- 当出现以下情形时,数组代表的是整个数组
// 1、- 在数组定义中
int buf1[5] = {0}; // 将整个数组里面的所有元素全部初始化为零,
// 数组在内存中不能随便移动位置(数组名不能进行加减操作:buf1 = buf1+1)(数组名相当于一个常量指针)
/*
解析:
buf1 = buf1 +1;
出现的问题:
error: assignment to expression with array type
// 翻译:错误,赋值的是一个数组类型的表达式
*/
// 2、- 在sizeof运算符表达式中
printf("数组buf1的大小为 == %lu个字节\n", sizeof(buf1));
/*
打印出来的数据为:
数组buf1的大小为 == 20个字节
解析:
sizeof是将其当成一个指针来对待吗??
如果将其当成了一个指针来对待的话,那么sizeof(buf1)的大小就应该为系统位数的大小
所以,sizeof(buf1)其实是算这个buf1数组的大小,而非指针的大小
*/
// 3、- 在取地址符(看其作用域即可知道其作用范围)
// a、代表数组buf1的首元素的地址
printf("数组buf1的首元素的地址 == %p\n", buf1);
printf("数组buf1的首元素的地址+1 == %p\n", buf1+1);
/*
解析:
数组buf1的首元素的地址 == 0x7ffeb38623c0
数组buf1的首元素的地址+1 == 0x7ffeb38623c4
说明:
buf1和buf1+1的地址相差4个地址(4个字节),所以能够说明
buf1代表的是首元素的地址
*/
// b、代表整个数组buf1的地址
printf("整个数组buf1的地址 == %p\n", &buf1);
printf("整个数组buf1的地址+1 == %p\n", &buf1+1);
/*
解析:
整个数组buf1的地址 == 0x7ffc24b773c0
整个数组buf1的地址+1 == 0x7ffc24b773d4
说明:
&buf1和&buf1+1的地址相差20个地址(20个字节),而数组buf1的大小正好是20个字节,所以能够说明
&buf1代表的是整个数组buf1的地址(其作用范围是20个字节)
*/
// (2)、- 当出现其他情形时,数组代表其首元素的地址
// 1、- 指针指向该数组首元素的地址时
int buf2[128] = {0};
int *p1 = buf2;
int *p2 = &buf2[0];
printf("数组buf2的首元素的地址 == %p\n", buf2);
printf("数组buf2的首元素的地址+1 == %p\n", buf2+1);
printf("p1的变量里面存放的地址 == %p\n", p1);
printf("p1+1的变量里面存放的地址 == %p\n", p1+1);
printf("p2的变量里面存放的地址 == %p\n", p2);
printf("p2+1的变量里面存放的地址 == %p\n", p2+1);
/*
解析:
数组buf2的首元素的地址 == 0x7fff58faf870
数组buf2的首元素的地址+1 == 0x7fff58faf874
p1的变量里面存放的地址 == 0x7fff58faf870
p1+1的变量里面存放的地址 == 0x7fff58faf874
p2的变量里面存放的地址 == 0x7fff58faf870
p2+1的变量里面存放的地址 == 0x7fff58faf874
说明:
指针p1和p2的移动地址和范围和buf2一样,所以证明指针指向该数组地址时,指向的是其数组首元素的地址
*/
// 2、- 函数传参时
char str[] = "abcdefghijklnmopqrstuvwxyz";
printf("原数组:%s\n", str);
upper_case(str, CAL_ARR_NUM(str));
printf("转换后:%s\n", str);
// 3、- 使用scanf函数
char buf3[128] = {0};
printf("请输入你想要输入的数据:\n");
// scanf("%s", &buf3); // buf3表示数组的首元素的地址(&buf3代表的是整个数组的地址,切记不要写&符号)
printf("buf3 == %s\n",buf3);
/*
解析:
: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char (*)[128]’ [-Wformat=]
翻译:要求是char *类型,但是实际类型是char(*)[128]类型
说明:
所以在使用scanf函数的使用,数组buf3前面不用家&符号,因为类型不匹配
*/
int a = 10; //在内存中申请了一段4个字节的连续的存储空间,用变量名a来访问这段内存空间
//数据占用内存空间的大小
printf("sizeof(a) = %ld\n",sizeof(a));
//用地址来描述这段内存空间
printf("&a = %p\n",&a); //16进制表示
return 0;
}
2. 字符串数组
字符串常量在内存中的存储,实质就是一个匿名数组
匿名数组,同样满足数组两种涵义的规定
格式:字符串常量后面默认带有 一个'\0'的结束符
char s[] = "hello"; //常用的字符串写法(字符数组)strlen=5 sizeof=6
char s1[] = {'h','e','l','l','o','\0'}; //表示的是字符串 strlen=5 sizeof=6
char s2[] = {"hello"}; //可以省略大括号
char s3[] = {'h','e','l','l','o'}; //表示的是字符序列 它不是字符串 strlen=无意义 sizeof=5
/*
字符数组的特别之处
#include <string.h>
size_t strlen(const char *s); //strlen里面必须填字符串的数据
字符串的两种表示新式:char *str char str[5]
然后char *str 和 char str[5]是等价的
说明:
数字0 和 字符'0'不是等价的
*/
char arr3[4] = {0};
printf("strlen(arr3) = %ld\n",strlen(arr3)); //0个有效字节
char arr4[4] = {'0','1','2'}; //字符数组的写法,但不是字符串的描述
printf("strlen(arr4) = %ld\n",strlen(arr4)); //3个有效字节
char arr5[4] = {'0','1','2','\0'}; //字符数组的写法,这个是字符串的描述(因为字符串末尾必须带一个'\0')
printf("strlen(arr5) = %ld\n",strlen(arr5)); //3个有效字节
char arr6[4] = "012"; //字符串"012" 和上面的arr5是等价的写法但是和arr4不是等价的写法
printf("strlen(arr6) = %ld\n",strlen(arr6)); //3个有效字节
//printf("strlen(arr1) = %ld\n",strlen(arr1)); //错误类型不匹配 真实的原因:和指针有关后面再讨论
3. 特殊数组
零长数组
概念:长度为0的数组,比如int data[0] // 相当于一个地址入口(很像指针),但是没有内存空间
用途:放在结构体的末尾,作为可变长度数组的入口
不定长数组
概念:数组buf[N]的长度不由里面的N决定,而由其初始化列表的元素个数决定
用途:不确定要赋值的数据的长度,又不想浪费内存空间,即可使用
变长数组(变量 --- 数组的长度由变量决定)
概念:定义时,使用变量作为元素个数的数组
要点:变长数组仅仅指元素个数在定义时是变量,而绝非指数组的长度可长可短,实际上,不管是普通数组还是变长数组,数组一旦定义完毕,其长度则不可改变
4. 二维数组以及与地址的关系
这块逻辑在上一篇博客有详细介绍
博客链接: C语言中的难点--地址与数组之间的关系解析-CSDN博客
5. 示例代码
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
//数组+函数
//数组传参的时候:数组名+数组元素个数
//参数:数组名+数组元素个数
void func(int arr[],int len)
{
int i = 0;
for(i = 0; i < len; i++)
printf("%d ",arr[i]);
printf("\n");
}
void func1(int arr[4])
{
int i= 0;
for(i = 0; i < 4; i++)
printf("%d ",arr[i]);
printf("\n");
}
int main(int argc, char const *argv[])
{
/*
int a[10] = {0};
int i = 0;
srand(time(0)); // 初始化随机种子
for(i = 0; i < 10; i++)
{
a[i] = rand()%100; //随机输出100以内的数赋值给a[i]
printf("%d ",a[i]);
}
//数组的越界访问 如果赋值超出合法范围 会有随时被篡改的风险
//数组的赋值
char arr[5]= {0}; //还没有想好赋值的时候会给覆盖掉
//定义+初始化
char arr1[5] = {'a','b','c','d','e'};//字符数组
short arr2[5] = {1,2,3,4,5,};
float arr3[5] = {1.1,2.2,3.3,4.4,5.5};
//先定义再初始化
int arr4[5] = {0};
arr4[0] = 1; //一般结合for循环赋值
//非常规赋值方法
int arr5[5] = {0}; //五个数都是0
int arr6[6] = {1,2}; //前两个数赋值后面的数默认赋值为0
int arr7[5] = {1,2,3,4,5,6,7}; //error 数的个数超过了范围
int arr8[5] = 0; //error
//数组的大小由你赋值的个数来决定
int arr9[] = {1,2,3,4,5};
// int arr10[]; //数组的大小迷失了 arr size missing
*/
// strlen()和sizieof()的区别
// 计算数组的长度(只能计算字符数组:strlen)和大小sizeof
char arr11[] = "hello world";
int len[5] = {1, 2, 3, 4, 5};
printf("sizeof(arr11) = %d\n", sizeof(arr11));
printf("strlen(arr11) = %d\n", strlen(arr11));
// printf("strlen(len) == %ld\n",strlen(len));//errro strlen不能计算证书数组长度
int a = 100;
//&:取址符(地址的大小是8个字节)随机值
printf("&a:%p\n", &a); //&a:0x7ffe47489ea4
char c = 'a';
printf("&c:%p\n", &c); // 如何去描述一个数据在计算机中的内存
int a1[3] = {0};
printf("&a1;%p\n", &a1); //取的是存放整个数组的地址
// 这个地址和上面的地址描述的含义不一样
// printf("a1;%p\n",a1);//这个是取地址数组a[0](等价于&a[0])的地址
// 打印数组的长度(只能打印字符数组(字符串)的长度)、大小和、地址
// char arr[4]
// short arr1[4]
// int arr2[4]

&spm=1001.2101.3001.5002&articleId=147190288&d=1&t=3&u=8e387bbe5ead47bdb4745b9dcbee30b6)
5243

被折叠的 条评论
为什么被折叠?



