C 基础 - 指针与数组
1. 指针概念
* 什么是指针
* swap函数交换
* geting获得整形
指针是一个(值为内存地址)的变量。
int x = 1, y = 2, z[10];
int *ip, *iq; /* 定义int型指针变量 */
ip = &x; /* ip现在指向x,值为x的内存地址 */
y = *ip; /* y的值为1 */
*ip = 0; /* x的值为0 */
ip = &z[0]; /* ip指向数组z的首地址 */
iq = ip; /* 把ip中的值赋值到iq */
注意:关于指针考虑的问题?
1. 该指针在内存中的地址是什么?
2. 该指针中存储的地址是什么?
1.1 swap(px, py)
指针的一个应用 交换两个数据
/* 交换两个变量的值 */
void swap (int *px, int *py)
{
int temp;
temp = *px;
*px = *py;
*py = temp;
}
/* 参数为a与b的地址 */
swap(&a, &b);
1.2 getint(int *pn) 获取Int整形
#include <ctype.h> int getch(void); void ungetch(int); /* * 接受自由格式的输入,并执行转换 * 将输入的字符流分解成整数 * 返回转换后得到的整数 */ int getint(int *pn) { int c, sign; /* 忽略所有空格 */ while (isspace(c = getch())) ; /* 不是数字,不是EOF, 不是+与- */ if(!isdigit(c) && c != EOF && c != '+' && c != '-') { ungetch(c); return 0; } /* 标志 */ sign = ( c == '-') ? -1 : 1; /* 如果是+或-,继续读入 */ if( c == '+' || c == '-') c = getch(); /* */ for( *pn = 0; isdigit(c), c = getch() ) *pn = 10 * *pn + (c - '0'); *pn *= sign; if(c != EOF) ungetch(c); return c; } /* 调用语句 */ int n, array[SIZE], getint(int *); for(n = 0; n < SIZE && (getint(&array[n]) != EOF); n++)
问题:数组与指针的区别?
数组名是常量,指针是变量
数组为什么不执行数组下标有效性检查?
结论:C语言对数组不执行下标的有效性检查。
理由: http://blog.csdn.net/u014787464/article/details/40077409
2. 一维数组
* 打印每月天数
* 数组求极值
* 数组逆序
2.1 使用数组打印每月天数
/*day_mon.c -- 打印每月的天数*/ #include <stdio.h> #define MONTHS 12 #define MAX_LEN 80 int main(void) { /*使用const表示char为只读数据,不允许被修改*/ const char months[MONTHS][MAX_LEN] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "Nobember", "December"}; int days[MONTHS] = {31, 28, 31, 30, 31, 30, 31, 31, 30 ,31, 30, 31}; int index, year; /*程序输入年份*/ puts("Enter year: "); scanf("%d", &year); /*闰年二月29天,平年28天*/ if( (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) ) days[1] = 29; /*程序打印输出*/ puts("Month Day"); for(index=0; index<MONTHS; index++) printf("%s %d\n", months[index], days[index]); return 0; }
2.2 max_min 求极值
数组求最大值与最小值问题
// maxmin.c
#include <stdio.h>
#define N 10
void max_min(int a[], int n, int *max, int *min);
int main(void)
{
int b[N], i, big, small;
// 输入10个数
printf("Enter %d numbers: ", N);
for (i = 0; i < N; i++)
scanf("%d", &b[i]);
// 调用函数,传递指针
max_min(b, N, &big, &small);
printf("Largest: %d\n", big);
printf("Smallest: %d\n", small);
return 0;
}
void max_min(int a[], int n, int *max, int *min)
{
int i;
*max = *min = a[0];
for (i = 1; i < n; i++) {
if (a[i] > *max)
*max = a[i];
else if (a[i] < *min)
*min = a[i];
}
}
2.3 数组逆序
数组的逆序
#include <stdio.h>
#define N 10
main()
{
int a[N], *p;
// 输入10个数
printf("Enter %d numbers: ", N);
for (p = a; p < a + N; p++)
scanf("%d", p);
// 逆向输出
printf("In reverse order:");
for (p = a + N - 1; p >= a; p--)
printf(" %d", *p);
printf("\n");
return 0;
}
在整形数组中,当数组未赋值时,其值是不固定的,当没有完全赋值时,值为0.
C99新规定,可以这样初始化数组:
int arr[6] = { [5] = 212 }; //把arr[5]初始化
3. 多维数组
* rain.c 使用多维数组计算
* 数组与指针
/*rain.c -- 针对若干年的降水量,计算年隆水总量、年降水平均量,以及月降水平均量*/
#include <stdio.h>
#define MONTHS 12 //一年的月份数
#define YEARS 5 //降水量数据的年数
int main(void)
{
/*把数组初始化为2010年到2014年降水量数据*/
const float rain[YEARS][MONTHS]={
{4.3,4.3,4.3,3.0,2.0,1.2,0.2,0.2,0.4,2.4,3.5,6.6},
{8.5,8.2,1.2,1.6,2.4,0.0,5.2,0.9,0.3,0.9,1.4,7.3},
{9.1,8.5,6.7,4.3,2.1,0.8,0.2,0.2,1.1,2.3,3.6,8.4},
{7.2,9.9,8.4,3.3,1.2,0.8,0.4,0.9,0.6,1.7,4.3,6.2},
{7.5,5.6,3.8,2.8,3.8,0.2,0.0,0.0,0.0,1.3,2.6,5.2}
};
int year,month;
float subtot, total;
printf(" YEAR RAINFALL (inches) \n");
for(year = 0, total = 0; year < YEARS; year++)
{
for(month = 0,subtot = 0; month < MONTHS; month++)
{
subtot += rain[year][month];
}
printf("%5d %15.1f\n", 2010+year, subtot);
total += subtot;
}
printf("\nThe yearly average is %.1f inches. \n\n ", total/YEARS);
printf("MONTHLY AVERAGES: \n\n");
printf("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec\n");
for(month = 0; month < MONTHS; month++)
{
for(year=0,subtot=0; year<YEARS;year++)
subtot += rain[year][month];
printf("%4.1f", subtot/YEARS);
}
printf("\n");
return 0;
}
一维数组是排成一行的数据
二维数组是放在平面上的数据
三维数组是把平面数据一层一层垒起来
3.1 指针和数组
指针提供了一种使用内存地址的符号的方法。
int a[100];
a == &a[0] //数组名同时也是该数组首元素的地址。
& 可以找到变量的地址
* 可以找到对象的值
/*pnt_add.c -- 指针加法*/ #include <stdio.h> #define SIZE 4 int main(void) { short dates[SIZE]; /* 定义dates数组 */ short * pti; // 声明指针变量 short index; double bills[SIZE]; double * ptf; // ptf是指向double型对象的指针 pti = dates; //把数组地址赋给指针 ptf = bills; printf("%23s %10s\n", "short", "double"); for(index=0; index<SIZE; index++) printf("pointers + %d: %10p %10p\n", index, pti+index, ptf+index); return 0; }
指针的数值就是它所指向的对象的地址。
在指针前运用运算符*就可以得到该指针所指向的对象的数值。
对指针加1,等价于对指针的值加上它指向的对象的字节大小。
dates + 2 == &date[2] //相同的地址
* (dates + 2) == dates[2] //相同的值
3.2 函数、数组和指针
/*sum_arr2.c -- 对一个数组的的元素求和*/
#include <stdio.h>
#define SIZE 10
int sump(int * start, int * end);
int main(void)
{
int marbles[SIZE] = {20, 10, 5, 39, 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;
}
可以把上面的循环整成一行代码
total += *start++
* 与 ++ 优先级相同,但结合性从右至左。 ++位于start之后,表示先将start加到total上,在执行start++
例如如下
*p++ = val; /* 将val压入栈 */ val = *--p; /* 将栈顶元素弹出到val中 */
/*order.c -- 指针运算的优先级*/
#include <stdio.h>
int data[2] = {100, 200};
int moredate[2] = {300, 400};
int main(void)
{
int * p1, * p2, * p3;
p1 = p2 = data;
p3 = moredate;
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);
return 0;
}
3.3 指针操作
/*
* 程序清单10.13
* ptr_ops.c - 指针操作
*
*/
#include <stdio.h>
int main(void)
{
int urn[5] = {100, 200, 300, 400, 500};
int * ptr1, * ptr2, * ptr3;
/*打印五个数在内存中的地址*/
for(int i=0; i<5; i++)
{
printf("%p\n", &urn[i]);
}
ptr1 = urn;
ptr2 = &urn[2];
printf("Pointer value, dereferenced pointer, pointer address: \n");
printf("ptr1 = %p, *ptr1 = %d, &ptr1 = %p \n", ptr1, *ptr1, &ptr1);
ptr3 = ptr1 + 4;
printf("\nadding an int to a pointer: \n");
printf("ptr1 + 4 = %p, *(ptr1 + 3) = %d\n", ptr1+4, *(ptr1 + 3));
ptr1++;
printf("\nvalues after ptr1++ \n");
printf("ptr1 = %p, *ptr1 = %d, &ptr1 = %p\n", ptr1, *ptr1, &ptr1);
ptr2--;
printf("\nvalues after --ptr2\n");
printf("ptr2 = %p, *ptr2 = %d, &ptr2 = %p\n", ptr2, *ptr2, &ptr2);
--ptr1;
++ptr2;
printf("Ponters reset to original values: \n");
printf("ptr1 = %p, ptr2 = %p\n", ptr1, ptr2);
printf("\nsubstracting one pointer from another: \n");
printf("ptr2 = %p, ptr1 = %p, ptr2 - ptr1 = %ld\n", ptr2, ptr1, ptr2 - ptr1);
printf("\nsubstracting an int from a pointer: \n");
printf("ptr3 = %p, ptr3 - 2 = %p\n", ptr3 ,ptr3-2);
return 0;
}
0x7fff560010e0
0x7fff560010e4
0x7fff560010e8
0x7fff560010ec
0x7fff560010f0
Pointer value, dereferenced pointer, pointer address:
ptr1 = 0x7fff560010e0, *ptr1 = 100, &ptr1 = 0x7fff560010d0
adding an int to a pointer:
ptr1 + 4 = 0x7fff560010f0, *(ptr1 + 3) = 400
values after ptr1++
ptr1 = 0x7fff560010e4, *ptr1 = 200, &ptr1 = 0x7fff560010d0
values after --ptr2
ptr2 = 0x7fff560010e4, *ptr2 = 200, &ptr2 = 0x7fff560010c8
Ponters reset to original values:
ptr1 = 0x7fff560010e0, ptr2 = 0x7fff560010e8
substracting one pointer from another:
ptr2 = 0x7fff560010e8, ptr1 = 0x7fff560010e0, ptr2 - ptr1 = 2
substracting an int from a pointer:
ptr3 = 0x7fff560010f0, ptr3 - 2 = 0x7fff560010e8
指针操作:赋值,可以把一个地址赋值给一个指针
求值或取值
取指针地址
将一个整数加给一个指针或从指针中减去一个数
增加指针的值或减小指针的值
求差值
比较
#define ALLOCSIZE 10000 /* 可用空间大小 */ static char allocbuf[ALLOCSIZE]; /* alloc存储空间 */ static char *allocp = allocbuf; /* 下一个空闲地址 */ /* * 分配n字节大小空间 * 返回n字节大小首地址 * 如果空间不足,返回0 */ char *alloc(int n) { if(allocbuf + ALLOCSIZE - allocp >= n) { allocp += n; return allocp - n; } else { /* 没有那么多空间 */ return 0; } } /* * 释放以p为首字节的内存 */ void afree(char *p) { /* 如果p在allocbuf之间 */ if(p >= allocbuf && p < allocbuf + ALLOCSIZE) allocp = p; /* 将p赋值给allocp */ }
3.4 保护数组的内容
如果设计意图是函数不改变数组的内容,可以在函数原型和定义的形式参数前加const
/*arf.c -- 处理数组的函数*/
#include <stdio.h>
#define SIZE 5
void show_array(const double ar[], int n);
void mult_array(double ar[], int n, double mult);
int main(void)
{
double dip[SIZE] = {20.0, 17.66, 8.2, 15.3, 22.22};
printf("The original dip array: \n");
show_array(dip, SIZE);
mult_array(dip, SIZE, 2.5);
printf("The dip array after calling mult_array(): \n");
show_array(dip, SIZE);
return 0;
}
/*显示数组内容*/
void show_array(const double ar[], int n)
{
int i;
for(i=0; i<n; i++)
printf("%8.3f", ar[i]);
putchar('\n');
}
/*用同一乘数去乘每个数组元素*/
void mult_array(double ar[], int n, double mult)
{
int i;
for(i=0; i<n; i++)
ar[i] *= mult;
}
3.5 指针和多维数组
int zippo[4][2]
zippo = &zippp[0], &zippo[0] = &zippo[0][0],zippo[0]是一个整数大小对象的地址,zippo是两个整数大小的对象的地址
zippo 指向两个大小的int, zippo[0]指向一个大小int, 因此zippo+1 和 zippo[0]+1的结果不同
*(zippo[0]) = zippo[0][0], *zippo = &zippo[0][0], **zippo = *(&zippo[0][0]) = zippo[0][0], zippo 是地址的地址,需要两次取值。
/*zippo1.c -- 有关zippo的信息*/
#include <stdio.h>
int main(void)
{
int zippo[4][2]={{2,4},{6,8},{1,3},{5,7}};
printf("zippo = %p, zippo + 1 = %p \n", zippo, zippo+1);
printf("zippo[0] = %p, zippo[0] + 1 = %p \n", zippo[0], zippo[0]+1);
printf("*zippo = %p, *zippo + 1 = %p \n", *zippo, *zippo+1);
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;
}
zippo = 0x7fff5a5240c0, zippo + 1 = 0x7fff5a5240c8
zippo[0] = 0x7fff5a5240c0, zippo[0] + 1 = 0x7fff5a5240c4
*zippo = 0x7fff5a5240c0, *zippo + 1 = 0x7fff5a5240c4
zippo[0][0] = 2
*zippo[0] = 2
**zippo = 2
zippo[2][1] = 3
*(*(zippo+2)+1) = 3
int (* pz)[2] //pz指向一个包含2个int值的数组, []优先级高于*
/*zippo2.c -- 通过一个指针变量获取有关zippo的信息*/
#include <stdio.h>
int main()
{
int zippo[4][2] = {{2,4},{6,8},{1,3},{5,7}};
int (*pz)[2];
pz = zippo;
printf("pz=%p, pz+1=%p\n", pz, pz+1);
printf("pz[0]=%p, pz[0]+1=%p\n", pz[0], pz[0]+1);
printf("*pz=%p, *pz+1=%p\n", *pz, *pz+1);
printf("*pz[0][0]=%d\n", pz[0][0]);
printf("*pz[0]=%d\n", *pz[0]);
printf("**pz=%d\n", **pz);
printf("pz[2][1]=%d\n", pz[2][1]);
printf("*(*(pz+2)+1)=%d\n", *(*(pz+2)+1));
return 0;
}
pz=0x7fff5020c0d0, pz+1=0x7fff5020c0d8
pz[0]=0x7fff5020c0d0, pz[0]+1=0x7fff5020c0d4
*pz=0x7fff5020c0d0, *pz+1=0x7fff5020c0d4
*pz[0][0]=2
*pz[0]=2
**pz=2
pz[2][1]=3
*(*(pz+2)+1)=3
总结:
创建数组的方法:
1. 声明数组时,用常量表达式表示数组的长度,用数组名访问数组的元素。使用静态内存或自动内存创建这种数组。
2.声明变长数组时,用变量表达式表示数组的长度,用数组名访问数组的元素。在自动内存中创建。
3. 声明一个指针,调用malloc,将其返回值赋给指针,使用指针访问数组的元素。该指针可以是静态或自动的。
int n;
double *ptd;
ptd = (double *)malloc(n * sizeof(double));
要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。
这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。典型的类似代码如下:
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa66;
*(int * const)(0x67a9) = 0xaa66;