C语言的固执

使用const声明数组:有时需要把数组设置为只读。这样,程序就只能从数组中检索值,不能把新值写入数组中。

const int days[5] = {1, 2, 3, 4, 5};

数值示例:

#include<stdio.h>

int main(void) {
	const int days[] = {31, 28, 31, 30, 31, 30 ,31 ,31 ,30 ,31};
	int index;
	
	//sizeof days是整个数组的大小,sizeof days[0]是数组中一个元素的大小 
	for(index=0; index<sizeof days / sizeof days[0]; index++){
		printf("Month %2d has %d days.\n", index+1, days[index]);
	}
	
	return 0;
}

数组边界:在使用数组时,要确保程序中使用的数组的下标在规定范围内,因为编译器不会检查出这种错误(可能发出警告)。

 

#include<stdio.h>

int main(void) {
	int value_1 = 44;
	int arr[4];
	int value_2 = 88;
	int i;
	
	printf("value_1=%d, value_2=%d\n", value_1, value_2);
	for(i=-1; i<=4; i++){
		arr[i] = 2 * i + 1;
	}
	for(i=-1; i<7; i++){
		printf("%2d %d\n", i, arr[i]);
	}
	printf("value_1=%d, value_2=%d\n", value_1, value_2);
	printf("address of arr[-1]: %p\n", &arr[-1]);
	printf("address of arr[4]: %p\n", &arr[4]);
	printf("address of value_1: %p\n", &value_1);
	printf("address of value_2: %p\n", &value_2);
}

变相使用指针:数组名是数组首元素的地址。也就是说,如果filizny是一个数组,下面的语句成立。

 

filizny == &filizny[0];//数组名是该数组首元素的地址

filizny和&filizny[0]都表示数组首元素的内存地址(*是地址运算符)。两者都是常量,在程序的运行过程中不会改变。但是,可以把它们赋值给指针变量,然后可以修改指针变量的值。(转换说明%p通常以十六进制显示指针的值)

 

指针和数组:

 

#include<stdio.h>
#define SIZE 4
int main(void) {
	short dates[SIZE];
	short * pti;
	short index;
	double bills[SIZE];
	double * ptf;
	pti = dates; //把数组地址赋给指针
	ptf = bills;
	
	printf("%23s %15s\n", "short", "double");
	for(index=0; index<SIZE; index++){
		printf("pointers + %d: %10p %10p\n", index, pti+index, ptf+index);
	}
	
	return 0;
}

以下关系表明了数组和指针的关系十分密切,可以使用指针标识数组的元素和获得元素。从本质上看,同一个对象有两种表示法。实际上C语言标准在描述数组表示法时确实借助了指针。也就是说,定义ar[n]的意思是*(ar+n)。可以认为*(ar+n)的意思是"到内存的ar位置,然后移动n个单元,检索存储在那里的值"。

dates+2 == &dates[2]   //相同的地址
*(dates+2) == dates[2] //相同的值

不要混淆*(dates+2)和*dates+2。间接运算符(*)的优先级高于+,所以*dates+2相当于(*dates)+2:

*(dates+2) //dates第3个元素的值
*dates+2   //dates第1个元素的值加2

示例:

 

#include<stdio.h>
#define MONTHS 12
int main(void) {
	int days[MONTHS] = {31, 28, 31, 30, 31, 30 ,31 ,31 ,30 ,31, 30, 31};
	int index;
	
	for(index=0; index<MONTHS; index++){ 
	//*(days+index)与days[index]相同 
		printf("Month %2d has %d days.\n", index+1, *(days+index));
	}
	
	return 0; 
}

声明数组形参:

因为数组名是该数组首元素的地址,作为实际参数的数组名要求形式参数是一个与之匹配的指针。只有在这种情况下,C才会把int ar[]和int * ar解释成一样。也就是说,ar是指向int的指针。由于函数原型可以省略参数名,下面4种原型都是等价的。

 

int sum(int *ar, int n);
int sum(int *, int);
int sum(int ar[], int n);
int sum(int [], int);

指针形参:

该程序表明了指针形参是变量,这意味着可以用索引表名访问数组中的哪一个元素。

指针start开始执行marbles数组的首元素,所以赋值表示total+=*start把首元素(20)加给total。然后表达式start++递增指针变量start,使其指向数组的下一个元素。因为start是指向int的指针,start递增1相当于其递增int类型的大小。

 

#include<stdio.h>
#define SIZE 10
int sump(int *start, int *end);

int main(void){
	int marbles[SIZE] = {20, 10, 5, 39, 4, 16, 19, 26, 31, 20};
	long answer;
	
	answer = sump(marbles, marbles+SIZE);
	printf("The total number of marbles is %ld.\n", answer);
	
	return 0;
}

//使用指针算法 
int sump(int *start, int *end){
	int total=0;
	
	while(start<end){
		total += *start; //把数组元素的值加起来 
		start++; //让指针指向下一个元素 
	}
	
	return total;
}

指针运算中的优先级:

 

#include<stdio.h>
int data[2] = {100,200};
int moredata[2] = {300,400};

int main(void){
	int *p1, *p2, *p3;
	
	p1 = p2 = data;
	p3 = moredata;
	printf("*p1=%d,   *p2=%d,   *p3=%d\n", *p1,*p2,*p3);
	printf("*p1++=%d, *++p2=%d, (*p3)++=%d\n", *p1++,*++p2,(*p3)++);
	printf("*p1=%d,   *p2=%d,   *p3=%d\n", *p1,*p2,*p3);
}

解引用未初始化的指针:

 

int * pt; //未初始化的指针
*pt = 5;  //严重的错误

为何不行?第2行的意思是把5储存在pt指向的位置。但是pt未被初始化,其值是一个随机值,所以不知道5将储存在何处。这可能不会出什么错,也可能会擦写数据或代码,导致程序崩溃。切记:创建一个指针时,系统只分配了储存指针本身的内存,并未分配储存数据的内存。

 

const的其他用法:

声明一个不能指向别处的指针。
double rates[2] = {1, 2, 3};
double * const pc = rates; //pc指向数组的开始
pc = &rates[2]l; //不允许,因为该指针不能指向别处
*pc = 92.99 //没问题--更改rates[0]的值

声明一个不能更改它所指向的地址,也不能修改指向地址上的值

double rates[2] = {1, 2, 3};
const double * const pc = rates; //pc指向数组的开始
pc = &rates[2]l; //不允许
*pc = 92.99 //不允许

指针和多维数组:

 

#include<stdio.h>

int main(void){
    	int zippo[4][2] = { {2, 4}, {6, 8}, {1, 3}, {5, 7}};
	
    	printf("zippo[0][0] = %d\n", zippo[0][0]);
    	printf("*zippo[0] = %d\n", *zippo[0]);
    	printf("**zippo = %d\n", **zippo);
    	printf("zippo[2][1] = %d\n", zippo[2][1]);
	printf("*(*(zippo+2)+1) = %d\n", *(*(zippo+2)+1));
	
	return 0;vv
}

注意:

特别注意,与zippo[2][1]等价的指针表示法是*(*(zippo+2) +1)。
zippo <-二维数组首元素的地址
zippo+2 <-二维数组的第3个元素(即一维数组)的地址
*(zippo+2) <-二维数组的第3个元素(即一维数组)的首元素地址
*(zippo+2) + 1 <-二维数组的第3个元素(即一维数组)的第二个元素地址
*(*(zippo+2)+1) <-二维数组的第3个一维数组元素的第2个int类型元素的值,即数组的第3行第2列的值

变长数组(VLA):声明一个带二维变长数组参数的函数。

 

int sum(int rows, int cols, int ar[rows][cols]); //ar是一个变长数组
或
int sum(int, int, int ar[*][*]);

其函数的定义如下:
int sum(int rows, int cols, int ar[rows][cols]){
    int r, c;
    int tot = 0;
    
    for(r=0; r<rows; r++){
        for(c=0; c<cols; c++){
            tot += ar[r][c];
        }
    }
    return tot;
}
假设声明了下列数组:
int array1[5][4];
int array2[100][4];
int array3[2][4];
tot = sum2d(array1, 5); //5x4数组的元素之和
tot = sum2d(array2, 100); //100x4数组的元素之和
tot = sum2d(array3, 2); //2x4数组的元素之和

运用VLS:

 

#include<stdio.h>
#define ROWS 3
#define COLS 4
int sum2d(int rows, int cols, int ar[rows][cols]);

int main(void){
	int i, j;
	int rs=3;
	int cs=10;
	int junk[ROWS][COLS]={
		{2, 4, 6, 8},
		{3, 5, 7, 9},
		{12, 10, 8, 6}
	};
	
	int morejunk[ROWS-1][COLS+2]={
		{20, 30, 40, 50, 60, 70},
		{5, 6, 7, 8, 9, 10}
	};
	
	int varr[rs][cs]; //变长数组(VLA)
	
	for(i=0; i<rs; i++){
		for(j=0; j<cs; j++){
			varr[i][j] = i*j+j;
		}
	} 
	
	printf("3x5 array\n");
	printf("Sum of all elements = %d\n", sum2d(ROWS, COLS, junk));
	
	printf("2x6 array\n");
	printf("Sum of all elements = %d\n", sum2d(ROWS-1, COLS+2, morejunk));
	
	printf("3x10 array\n");
	printf("Sum of all elements = %d\n", sum2d(rs, cs, varr));
	
	return 0;
}

//带变长数组形参的函数
int sum2d(int rows, int cols, int ar[rows][cols]){
	int r, c;
	int tot=0;
	
	for(r=0; r<rows; r++){
		for(c=0; c<cols; c++){
			tot += ar[r][c];
		}
	}
	
	return tot;
}

复合字面量:

 

数组声明:int diva[2] = {10, 20};
下面的复合字面量创建了一和diva数组相同的匿名数组,也有两个int类型的值:
(int [2]){10, 20}; //复合面量
注意,去掉声明中的数组名,留下的int[2]即是复合字面量的类型名
也可以
(int []){50, 20, 90}; //内含3个元素的复合字面量

因为复合字面量是匿名的,所以不能先创建然后再使用它,必须在创建的同时使用它。使用指针记录地址就是一种用法:

int * pt1;
pt1 = (int [2]){10, 20};

还可以把复合字面量作为实际参数传递给带有匹配形式参数的函数:

int sum(const int ar[], int n);
....
int total3;
total3 = sum((int []){4,4,4,5,5,5}, 6);

创建二维int数组并储存其地址:

 

//声明一个指向二维数组的指针,该数组内含2个数组元素
int (*pt2)[4];
//每个元素是内含4个int类型值的数组
pt2=(int [2][4]){ {1,2,3,-9}, {4,5,6-8}};

最后:

记住,复合字面量是提供只临时需要的值的一种手段。复合字面量具有块作用域,这意味着一旦离开定义复合字面量的块,程序将无法保证该字面量是否存在。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值