一、前言
指针是C语言中的一个重要概念,也是C语言的一个重要特色。正确而灵活地运用它,可以有效地表示复杂的数据结构;能动态分配内存;方便地使用字符串;有效而方便地使用数组;在调用函数时能获得1个以上的结果;能直接处理内存单元地址等,这对设计系统软件是非常必要的。掌握指针的应用,可以使程序简洁、紧凑、高效。每一个学习和使用C语言的人,都应当深入地学习和掌握指针。可以说,不掌握指针就是没有掌握C的精华。
二、地址和指针的概念
变量的属性:名、值和地址
变量的访问方式:直接访问、间接访问
变量的地址称为该变量的“指针”
指针变量是存放地址的变量
#include <stdio.h>
#define CHANGE 1
int main(int argc, char *argv[]) {
int i,j;
int *i_pointer,*j_pointer; // 创建指针变量
i_pointer = &i; // 变量的地址:该变量的指针
j_pointer = &j;
// printf("%d\n",sizeof(i_pointer));
printf("i=%d\n",i); // 通过变量访问:直接访问
printf("i=%d\n",*i_pointer); // 通过指针变量访问:间接访问 *i_pointer = i
return 0;
}
三、变量的指针和指向变量的指针变量
变量的指针就是变量的地址。存放变量地址的变量是指针变量,它用来指向另一个变量。为了表示指针变量和它所指向的变量之间的联系,在程序中用“*”符号表示“指向”。如果已定义i_pointer为指针变量,则(*i_pointer)是i_pointer所指向的变量。
#include <stdio.h>
int main(int argc, char *argv[]) {
int i,j;
int *i_pointer,*j_pointer; // 创建指针变量
i = 12;
i_pointer = &i; // 指向整型变量i,i_pointer存放的是变量i的地址
j_pointer = &j;
printf("i=%d\n",i); // 通过变量访问:直接访问
printf("*i_pointer=%d\n",*i_pointer); // 通过指针变量访问:间接访问 *i_pointer = i
return 0;
}
3.1 定义一个指针变量
C语言规定所有变量在使用前必须定义,指定其类型,并按此分配内存单元。指针变量不同于整型变量和其他类型的变量,它是用来专门存放地址的,必须将它定义为“指针类型”。
定义指针变量的一般形式为
基类型 * 指针变量名;
可以用赋值语句使一个指针变量得到另一个变量的地址,从而使它指向一个该变量。
(1)指针变量前面的“*”表示该变量的类型为指针型变量
(2)在定义指针变量时必须指定基类型
3.2 指针变量的引用
指针变量中只能存放地址(指针),不要将一个整数(或其他非地址类型的数据)赋给一个指针变量。
有两个有关的运算符:
(1)&:取地址运算符
(2)*:指针运算符(或称“间接访问”运算符),取其指向的内容。
通过指针变量访问整型变量
#include <stdio.h>
int main(int argc, char *argv[]) {
int a, b;
int *pointer_1, *pointer_2;
a = 100;
b = 10;
pointer_1 = &a;
pointer_2 = &b;
printf("%d, %d\n", a, b);
printf("%d, %d\n", *pointer_1, *pointer_2);
return 0;
}
3.2.1 “&” 和"*"说明
输入a和b两个整数,按先大后小的顺序输出a和b
#include <stdio.h>
int main(int argc, char *argv[]) {
int *p1, *p2, a, b;
scanf("%d,%d", &a, &b);
p1 = &a;
p2 = &b;
if (a < b) {
p1 = &b;
p2 = &a;
}
printf("a=%d,b=%d\n", a, b);
printf("max = %d,min = %d\n", *p1, *p2);
return 0;
}
3.3 指针变量作为函数参数
1、输入a和b两个整数,按先大后小的顺序输出a和b
#include <stdio.h>
void swap(int *x, int *y) {
// int *temp;
// int i;
// temp = &i;
// *temp = *x; // i= a
// *x= *y;// a= b
// *y = *temp; // b= i
int i;
i = *x;
*x = *y;
*y = i;
}
int main(int argc, char *argv[]) {
int a, b;
scanf("%d,%d", &a, &b);
if (a < b)
swap(&a, &b);
printf("max=%d min=%d\n", a, b);
return 0;
}
2、输入a、b、c这3个整数,按大小顺序输出
#include <stdio.h>
void swap(int *x, int *y) {
// int *temp;
// int i;
// temp = &i;
// *temp = *x; // i= a
// *x= *y;// a= b
// *y = *temp; // b= i
int i;
i = *x;
*x = *y;
*y = i;
}
void exchange(int *q1, int *q2, int *q3) {
if (*q1 < *q2)
swap(q1, q2);
if (*q1 < *q3)
swap(q1, q3);
if (*q2 < *q3)
swap(q2, q3);
}
int main(int argc, char *argv[]) {
int a, b, c;
scanf("%d,%d,%d", &a, &b, &c);
exchange(&a, &b, &c);
printf("%d %d %d\n", a, b, c);
return 0;
}
四、数组和指针
4.1 指向数组元素的指针
#include <stdio.h>
int main(int argc, char *argv[]) {
int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p;
p = a;
p = &a[9];// a[9]指针地址复制给指针p
printf("*p = %d\n", *p);
return 0;
}
4.2 通过指针引用数组元素
#include <stdio.h>
int main(int argc, char *argv[]) {
int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p;
p = a;
// p = &a[0];// a[9]指针地址复制给指针p
*p = 1;
printf("a[0]=%d\n", *p);
return 0;
}
4.3 输出数组中的全部元素
1、下标法
#include <stdio.h>
int main(int argc, char *argv[]) {
int a[10];
for (int i = 0; i < 10; i++) {
scanf("%d", &a[i]);
}
for (int i = 0; i < 10; i++) {
printf("%d\n", a[i]); // 下标法
}
return 0;
}
2、通过数组名计算数组元素地址,找出元素的值
#include <stdio.h>
int main(int argc, char *argv[]) {
int a[10];
for (int i = 0; i < 10; i++) {
scanf("%d", &a[i]);
}
for (int i = 0; i < 10; i++) {
printf("%d", *(a + i)); // 下标法
}
return 0;
}
3、 指针变量指向数组元素
#include <stdio.h>
int main(int argc, char *argv[]) {
int a[10];
int *p;
p = a;
for (int i = 0; i < 10; i++) {
scanf("%d", &a[i]);
}
for (int i = 0; i < 10; i++) {
printf("%d", *(p + i)); // 下标法
}
return 0;
}
4、通过指针变量输出a数组的10个元素
#include <stdio.h>
int main(int argc, char *argv[]) {
int a[10];
int *p;
p = a;
for (int i = 0; i < 10; i++) {
scanf("%d", &a[i]);
}
for (int i = 0; i < 10; i++) {
printf("%d", *(p + i)); // 下标法
}
return 0;
}
4.4 用数组名作函数参数
如果一个实参数组,要想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况
(1)形参和实参都用数组名
(2)实参用数组名,形参用指针变量
(3)实参形参都用指针变量
(4)实参为指针变量,形参为数组名
4.4.1 将数组a中n个整数按相反顺序存放
#include <stdio.h>
void inv(int x[], int n) {
int i, j;
for (i = 0, j = n - 1; i <= (n - 1) / 2; i++, j--) {
int temp;
temp = x[i]; // x[i]==*(x+i)
x[i] = x[j]; // x[j]==*(x+j)
x[j] = temp;
}
}
int main(int argc, char *argv[]) {
int a[10];
for (int i = 0; i <= 9; i++)
scanf("%d", &a[i]);
inv(a, 10);
for (int i = 0; i <= 9; i++)
printf("%d,", a[i]);
return 0;
}
4.4.2 用实参指针改写4.4.1
#include <stdio.h>
void inv(int *x, int n) {
int i, j;
for (i = 0, j = n - 1; i <= (n - 1) / 2; i++, j--) {
// int temp;
// temp = x[i]; // x[i]==*(x+i)
// x[i] = x[j]; // x[j]==*(x+j)
// x[j] = temp;
int temp;
temp = *(x + i);
*(x + i) = *(x + j);
*(x + j) = temp;
}
}
int main(int argc, char *argv[]) {
int a[10];
for (int i = 0; i <= 9; i++)
scanf("%d", &a[i]);
int *p;
p = a; // 指向数组首地址
// p = &a[0];
inv(p, 10);
for (int i = 0; i <= 9; i++)
printf("%d,", a[i]);
return 0;
}
4.4.3 用选择法对10个整数按由大到小顺序排序
#include <stdio.h>
void sort(int x[], int n) {
for (int i = 0; i < n - 1; i++) {
int p = i;
for (int j = i + 1; j < n; j++)
if (x[j] < x[p])
p = j;// 更新指针
if (p != i) {
int temp;
temp = x[p];
x[p] = x[i];
x[i] = temp;
}
}
}
int main(int argc, char *argv[]) {
int a[10];
int *p = a;
for (int i = 0; i < 10; i++)
scanf("%d", p++);
p = a; // 初始化首地址
sort(p, 10);
for (int i = 0; i < 10; i++)
printf("%d ", *(p + i));
return 0;
}
4.5 多维数组和指针
输出二维数组有关的值
#include <stdio.h>
int main(int argc, char *argv[])
{
int a[3][4] = {{1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23}};
printf("%x,%x\n", a, *a); //*a==a[0]
printf("%x,%x\n", a[0], *(a + 0)); //*(a+0)==a[0]
printf("%x,%x\n", &a[0], &a[0][0]); //&a[0]==a+0
printf("%x,%x\n", a[1], a + 1);
printf("%x,%x\n", &a[1][0], *(a + 1) + 0); //*(a+1)==a[1]
printf("%x,%x\n", a[2], *(a + 2)); //*(a+2)==a[2]
printf("%x,%x\n", &a[2], a + 2);
printf("%d,%d\n", a[1][0], *(*(a + 1) + 0));// a+1:第一行 + 0:第一行第0个元素
return 0;
}
4.5.1 指向多维数组元素的指针变量
4.5.1.1 指向数组元素的指针变量
用指针变量输出二维数组元素的值
#include <stdio.h>
int main(int argc, char *argv[])
{
int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
int *p; // 创建指针p
for (p = a[0]; p < a[0] + 11; p++)
{
if ((p - a[0]) % 4 == 0) // 每四个元素换行
printf("\n");
printf("%d ", *p);
}
printf("\n");
return 0;
}
4.5.1.2 指向由m个元素组成的一维数组的指针变量
输出二维数组任一行任一列的值
#include <stdio.h>
int main(int argc, char *argv[])
{
int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
int (*p)[4]; // p是一个指针变量,指向大小为4的一维数组
p = a;// 指向二维数组的第一行的一维数组
int i,j;
scanf("%d %d",&i,&j);
printf("%d\n", *(*(p+i)+j)); // p+i行, +j,第j列
return 0;
}
4.5.2 用指向数组的指针作函数参数
一维数组名可以作为函数参数传递,多维数组名也可作函数参数传递。在用指针变量作形参以接受实参数组名传递来的地址时,有两种方法:
1、用指向变量的指针变量
2、用指向一维数组的指针变量
4.5.2.1 案例一
有一个班,3个学生,各学4门课,计算总平均分数以及第n个学生的成绩
#include <stdio.h>
float average(float *p, int n)
{
float *start;
float sum = 0.0;
for (start = p; p <= start + n - 1; p++)
{ // 将指针p赋给start指针,start+n-1:一共的元素
sum += *p; // 统计分数和
}
return sum / n; // 返回平均分
}
void search(float (*p)[4], int n)
{ // p是一个指针变量,指向大小为4的一维数组
for (int i = 0; i < 4; i++)
{
printf("%f ", *(*(p + n) + i)); // p+n 第n行 +i 第i列
}
printf("\n");
}
int main(int argc, char *argv[])
{
float score[3][4] = {{65, 67, 70, 60}, {80, 87, 90, 81}, {90, 99, 100, 98}};
printf("average=%f\n", average(*score, 12)); //*score==score[0]
search(score, 2); // 行标是2的人的分数
return 0;
}
4.5.2.2 案例二
在上题基础上,查找有一门以上课程不及格的学生,输出他们的全部课程的成绩
#include <stdio.h>
void search(float (*p)[4], int n) { // p是一个指针变量,指向大小为4的一维数组
for (int i = 0; i < n; i++) {
int flag = 0;
for (int j = 0; j < 4; j++) {
if (*(*(p + i) + j) < 60.0) {
flag = 1;
break;
}
}
// 打印
if (flag == 1) {
printf("%d:\n",i);
for (int j = 0; j < 4; j++) {
printf("%f ", *(*(p + i) + j)); // 第几行第几列
}
printf("\n");
}
}
}
int main(int argc, char *argv[]) {
float score[3][4] = {{55, 67, 70, 60}, {50, 87, 90, 81}, {90, 99, 100, 98}};
search(score, 3); // 行标是2的人的分数
return 0;
}
4.6 字符串和指针
在C程序中,可以用两种方法访问一个字符串
4.1 用字符数组存放一个字符串,然后输出该字符串。
1、定义一个字符数组,对它初始化,然后输出该字符串
#include <stdio.h>
int main(int argc, char *argv[])
{
char string[] = "I love China!"; // !后面会自动加一个\0
printf("%s\n", string);
//string++;
return 0;
}
4.2 用字符指针指向一个字符串
4.2.1 定义字符指针
#include <stdio.h>
int main(int argc, char *argv[])
{
// char string[] = "I love China!"; // !后面会自动加一个\0
char *string = "I love China!";
printf("%s\n", string);
return 0;
}
4.2.2 将字符串a赋值为字符串b
#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
char a[] = "I am a boy.", b[20];
for (i = 0; *(a + i) != '\0'; i++)
{
*(b + i) = *(a + i);
}
*(b + i) = '\0'; // 字符串结束标识
for (i = 0; b[i] != '\0'; i++)
printf("%c", b[i]);
printf("\n");
printf("%s\n", b);
return 0;
}
4.2.3 用指针变量来处理例4.2.2问题
#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
char a[] = "I am a boy.", b[20];
char *p, *q;
for (p = a, q = b; *p != '\0'; p++, q++) // p,q分别指向数组a[]和b[]第一个元素
{
*q = *p;
}
*q = '\0';
q = b; // 重新初始化
printf("%s\n", q);
return 0;
}
4.2.4 字符指针作为函数参数
将一个字符串从一个函数传递到另一个函数,可以用地址传递的方法,即用字符数组名作参数,也可以用指向字符的指针变量作参数。在被调用的函数中可以改变字符串的内容,在主调函数中可以得到改变了的字符串。
用函数调用实现字符串的复制
4.2.4.1 用字符数组作参数
#include <stdio.h>
void copy_string(char from[], char to[])
{
int i = 0;
while (from[i] != '\0')
{
to[i] = from[i];
i++;
}
to[i] = '\0';
}
int main(int argc, char *argv[])
{
char a[] = "I am a teacher.";
char b[] = "You are student.";
printf("string a=%s string b=%s\n", a, b);
printf("copy string a to string b:\n");
copy_string(a, b);
printf("string a=%s string b=%s\n", a, b);
return 0;
}
4.2.4.2 形参用字符指针变量
#include <stdio.h>
void copy_string(char *from, char *to)
{
// 方式一
// for (; *from != '\0'; from++,to++)
// {
// *to = *from;
// }
// *to = '\0';
// 方式二
// while(*from != '\0'){
// *to = *from;
// from++;
// to++;
// }
// 方式三
// while (*from != '\0')
// {
// *to++ = *from++;
// }
// *to = '\0';
// 方式四
while(*to++ = *from++);
}
int main(int argc, char *argv[])
{
char a[] = "I am a teacher.";
char b[] = "You are student.";
printf("string a=%s string b=%s\n", a, b);
printf("copy string a to string b:\n");
char *from = a;
char *to = b;
copy_string(from, to);
printf("string a=%s string b=%s\n", a, b);
return 0;
}
4.2.5 使用字符指针变量和字符数组的讨论
虽然用字符数组和字符指针变量都能实现字符串的存储和运算,但它们两者之间是有区别的,不应混为一谈,主要有以下几点。
(1)字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址,决不是将字符串放到字符指针变量中。
(2)赋值方式。
(3)对字符指针变量赋初值。
(4)如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个字符变量的地址,也就是说,该指针变量可以指向一个字符型数据,但如果未对它赋予一个地址值,则它并未具体指向一个确定的字符数据。
(5)指针变量的值是可以改变的。
4.2.5.1 改变指针变量的值
int main(int argc, char *argv[])
{
char *a = "I love China!";
a = a + 7;
// char a[] = "I love China!";
// a = a + 7;
printf("%s", a);
return 0;
}
4.2.5.2 用带下标的字符指针变量引用字符串中的字符
用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。
int main(int argc, char *argv[])
{
char *a = "I love China!";
printf("The sixth character is %c\n", *(a + 5)); //a[5]==*(a+5)
for (int i = 0; *(a + i) != '\0'; i++)
printf("%c", a[i]);
printf("\n");
char *format = "a=%d,b=%d\n";
int aa = 1, bb = 2;
printf(format, aa, bb);
return 0;
}
五、指向函数的指针
5.1 函数指针变量调用函数
可以用指针变量指向整型变量、字符串、数组,也可以指向一个函数。一个函数在编译时被分配给一个入口地址。这个函数的入口地址就称为函数的指针。可以用一个指针变量指向函数,然后通过该指针变量调用此函数。
5.1.1 求a和b中的大者
#include <stdio.h>
int max(int a, int b)
{
if (a > b)
return a;
else
return b;
}
int min(int a, int b)
{
if (a > b)
return b;
else
return a;
}
int main(int argc, char *argv[])
{
int a, b;
scanf("%d %d", &a, &b);
printf("max=%d\n", max(a, b));
int (*p)(int,int); // (*p):指针变量---指向函数的类型--->(int,int):函数形参类型
p = max; // 指向函数首地址
printf("max=%d\n", (*p)(a, b));
return 0;
}
说明
(1)指向函数的指针变量的一般定义形式为:数据类型 (*指针变量名) (函数参数表列);
(2)函数的调用可以通过函数名调用,也可以通过函数指针调用(即用指向函数的指针表量调用)。
(3)“int (*p)(int,int);”表示定义一个指向函数的指针变量p,它不是固定指向哪一个函数的,而是表示定义了这样一个类型的变量,它是专门用来存放函数的入口地址的。
(4)在给函数指针变量赋值时,只需给出函数名而不必给出参数。
(5)用函数指针变量调用函数时,只需将(*p)代替函数名即可,在(*p)之后的括号中根据需要写上实参。
(6)对指向函数的指针变量,像p+n、p++等运算是无意义的。
5.2 指向函数的指针做函数参数
函数指针变量通常的用途之一是把指针作为参数传递到其他函数。函数的参数可以是变量、指向变量的指针变量、数组名、指向数组的指针变量等。函数的指针也可以作为参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。
5.2.1 案例:函数process,在调用它的时候,每次实现不同的功能
设一个函数process,在调用它的时候,每次实现不同的功能。输入a和b两个数,第一调用process时找出a和b中大者,第二次找出其中小者,第三次求a与b之和
#include <stdio.h>
int max(int a, int b)
{
if (a > b)
return a;
else
return b;
}
int min(int a, int b)
{
if (a > b)
return b;
else
return a;
}
int add(int a, int b)
{
return a + b;
}
void process(int a, int b, int (*p)(int, int)) // 指向函数的指针做函数参数
{
printf("%d\n", (*p)(a, b));
}
int main(int argc, char *argv[])
{
int a, b;
scanf("%d %d", &a, &b);
process(a, b, max);
process(a, b, min);
process(a, b, add);
return 0;
}
5.3 返回指针值的函数:类型名 * 函数名(参数表列);
一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即地址。其概念与以前类似,只是返回的值的类型是指针类型而已。
这种返回指针值的函数,一般定义形式为
类型名 * 函数名(参数表列);
5.3.1 指针函数来实现案例
有若干个学生的成绩(每个学生有4门课程),要求在用户输入学生序号后,能输出该学生的全部成绩。用指针函数来实现
#include <stdio.h>
int *search(int n, int (*p)[4]) // 一维数组指针类型
{
return *(p + n); //*(p+n)==score[n]
}
int main(int argc, char *argv[])
{
int score[][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
//score score[0] score[1] score[2]
int *p = search(1, score);
for (int i = 0; i < 4; i++)
printf("%d ", *(p + i));
return 0;
}
5.3.2 优化5.3.1
对上例中的学生,找出其中不及格课程的学生及其学生号
#include <stdio.h>
int *search(int (*p)[4])
{
for (int i = 0; i < 4; i++)
{
if (*(*p + i) < 60) // 当前一维数组*p + i第i个元素
{
return *(p + 1); // 指向下一个一维数组指针
}
}
return *p; // 一维数组
}
int main(int argc, char *argv[])
{
int score[][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
for (int i = 0; i < 3; i++)
{
int *p = search(score + i);
if (p != *(score + i)) // 行类型指针
{
printf("student number=%d\n", i);
for (int j = 0; j < 4; j++)
printf("%d ", *(*(score + i) + j));
printf("\n");
}
}
return 0;
}
5.4 指针数组和指向指针的指针
一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都相当于一个指针变量。一维指针数组的定义形式为
类型名 * 数组名[数组长度];
例如:int * p[4];
5.4.1 指针数组案例
将若干字符串按字母顺序输出
#include <stdio.h>
#include <string.h>
void sort(char *name[], int n) // 字符指针数组类型
{
for (int i = 0; i < n - 1; i++) // 选择法,n个元素需要n-1次排序
{
int p = i;
for (int j = i + 1; j < n; j++)
{
if (strcmp(name[j], name[p]) < 0)
{
p = j; // 改变指针的位置
}
}
if (p != i)
{
char *temp;
temp = name[i];
name[i] = name[p];
name[p] = temp;
}
}
}
void print(char *name[], int n)
{
for (int i = 0; i < n; i++)
{
printf("%s\n", name[i]);
}
}
int main(int argc, char *argv[])
{
char *name[5] = {"Follow me", "BASIC", "Great Wall", "FORTRAN", "Computer design"};
sort(name, 5);
print(name, 5);
return 0;
}
5.4.2 指向指针的指针案例
1、使用指向指针的指针
#include <stdio.h>
int main(int argc, char *argv[])
{
char *name[5] = {"Follow me", "BASIC", "Great Wall", "FORTRAN", "Computer design"}; // 字符指针的数组
char **p; // 指向字符指针的指针
// for (int i = 0; i < 5; i++)
// {
// p = name + i; // 字符指针的指针
// printf("%s\n", *p); // 第i个元素
// }
p = name + 1;
printf("%c\n",**p);
printf("%c\n", *(*p+1)); // *p--->name[1]
return 0;
}
2、指针数组的元素指向整型数据
#include <stdio.h>
int main(int argc, char *argv[])
{
int a[5] = {1, 2, 3, 4, 5};
int *num[5] = {&a[0], &a[1], &a[2], &a[3], &a[4]}; // 指针数组
int **p; // 指向指针的指针
p = num; // 指针数组的第一个元素
for (int i = 0; i < 5; i++)
{
printf("%d ", **p);
p++;
}
return 0;
}
5.4.3 指针数组作main函数的形参
指针数组的一个重要应用是作为main函数的形参。在以往的程序中,main函数的第一行一般写成以下形式:void main()
括号中是空的。实际上,main函数可以有参数,例如:void main(int argc, char * argv[])
5.4.3.1
#include <stdio.h>
int main(int argc, char *argv[]) // *argv[]:指针数组
{
for (int i = 0; i < argc; i++)
{
printf("%s\n", argv[i]);
}
getchar();
return 0;
}
六、有关指针的数据类型和指针运算的小结
6.1 有关指针的数据类型的小结
6.2 指针运算小结
(1)指针变量加(减)一个整数
#include <stdio.h>
int main(int argc, char *argv[]) // *argv[]:指针数组
{
int a[5] = {1, 2, 3, 4, 5};
int *p;
p = a;
//printf("%x\n",*p);
printf("%x\n", p);
p++;
printf("%x\n", p);
return 0;
}
(2)指针变量赋值
#include <stdio.h>
int main(int argc, char *argv[]) // *argv[]:指针数组
{
int a[5] = {1, 2, 3, 4, 5};
int *p;
p = a;
int i = 5;
p = &i;
printf("%d\n",*p);
return 0;
}
(3)指针变量可以有空值,即该指针变量不指向任何变量
#include <stdio.h>
int main(int argc, char *argv[]) // *argv[]:指针数组
{
int a[5] = {1, 2, 3, 4, 5};
int *p;
p = NULL;
// if(p == NULL){
// }else
// {
// /* code */
// }
printf("%x\n", p);
return 0;
}
(4)两个指针变量可以相减
#include <stdio.h>
int main(int argc, char *argv[]) // *argv[]:指针数组
{
int a[5] = {1, 2, 3, 4, 5};
int *p1, *p2;
p1 = &a[0];
p2 = &a[1];
printf("p2-p1 = %d\n", (p2-p1));
return 0;
}
(5)两个指针变量比较
#include <stdio.h>
int main(int argc, char *argv[]) // *argv[]:指针数组
{
int a[5] = {1, 2, 3, 4, 5};
int *p1, *p2;
p1 = &a[0];
p2 = &a[4];
printf("p2==p1 = %d\n", p2==p1);
return 0;
}
6.3 void指针类型
#include <stdio.h>
#include <malloc.h>
int main(int argc, char *argv[]) // *argv[]:指针数组
{
char *string;
string = NULL;
string = (char *)malloc(5);
if (string == NULL)
printf("Insufficient memory available\n");
else
{
*(string + 0) = 'a';
*(string + 1) = 'b';
*(string + 2) = 'c';
*(string + 3) = 'd';
*(string + 4) = '\0';
printf("%s\n", string);
free(string);
printf("Memory freed\n");
}
return 0;
}
七、位运算
7.1 位运算符和位运算
7.1.1 按位与运算符“&”
7.1.2 按位或“|”
7.1.3 异或运算符:“^”
#include <malloc.h>
#include <stdio.h>
// 主函数
int main(int argc, char *argv[]) // *argv[]:指针数组
{
int a = 3;
int b = 4;
a = a ^ b;
b = b ^ a;
a = a ^ b;
printf("a=%d b=%d\n", a, b);
return 0;
}
7.1.4 取反运算符“~”
int a = 122388;
int c = ~a;
printf("a=%d\n", c);
7.1.5 左移运算符:“<<”
#include <malloc.h>
#include <stdio.h>
// 主函数
int main(int argc, char *argv[]) // *argv[]:指针数组
{
int a = 2;
int c =a<<1;
printf("a=%d\n", c);
return 0;
}
7.1.6 右移运算符:“>>”
7.1.7 位运算赋值运算符
int a = 0x80000001;
a = a>>1;
a>>=1;
7.1.8 不同长度的数据进行位运算
7.1.9 位段
实际上,有时存储一个信息不必用一个或多个字节,例如,“真”或“假”用0或1表示,只需1位即可。在计算机用于过程控制、参数检测或数据通信领域时,控制信息往往只占一个字节中的一个或几个二进制位,常常在一个字节中放几个信息。
向一个字节中的一个或几个二进制位赋值和改变它的值,可以用以下两种方法:
(1)可以人为地将一个整型变量data分为几部分。
(2)段位结构体
C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这样以位为单位的成员称为“位段”或称“位域”。利用位段能够用较少的位数存储数据。例如:
struct packed_data
{
unsigned a:2;
unsigned b:6;
unsigned c:4;
unsigned d:4;
int i;
} data;
#include <malloc.h>
#include <stdio.h>
struct packed_data
{
unsigned int a : 2; // 占用两个二进制位
unsigned int b : 3;
unsigned int c : 4;
int i;
}data;
// 主函数
int
main(int argc, char *argv[]) // *argv[]:指针数组
{
data.a = 3;
data.b = 7;
data.c =15;
data.i = 2345678;
printf("%d %d %d %d\n", data.a, data.b, data.c, data.i);
return 0;
}
说明
案例
1、编写一个函数getbits,从一个16位的单元中取出某几位(即该几位保留原值,其余位为0)。函数调用形式为getbits(value, n1, n2),value为该16位(2个字节)中的数据值,n1为欲取出的起始位,n2为欲取出的结束位,例如:getbits(0101675,5,8)表示对八进制101675这个数,取出它的从左面起第5位到第8位。
#include <malloc.h>
#include <stdio.h>
unsigned short getbits(unsigned short int value, int n1, int n2)
{
return (unsigned short int)(value << (n1 - 1)) >> (16 - (n2 - n1 + 1));
//00000000 00000000 00000000 10000011
}
// 主函数
int main(int argc, char *argv[]) // *argv[]:指针数组
{
unsigned short int a;
printf("%x\n", getbits(0101675, 5, 8));
return 0;
}
2、 编写一函数用来实现左右循环移位。函数名为move,调用方法为move(value, n),其中value为要循环位移的数,n为位移的位数。例如,n<0表示为左移;n>0为右移。n=4表示要右移4位;n=-3表示要左移3位。
#include <malloc.h>
#include <stdio.h>
unsigned int move(unsigned int value, int n)
{
unsigned int t1, t2;
if (n > 0)
{
t1 = value << (32 - n);
t2 = value >> n;
}
else
{
t1 = value >> (32 + n); // 负数
t2 = value << (-n);
}
return t1 | t2;
}
// 主函数
int main(int argc, char *argv[]) // *argv[]:指针数组
{
unsigned int a;
a = 0x12345678;
printf("%x\n", a);
printf("right>>%x\n", move(a, 4));
printf("left<<%x\n", move(a, -4));
return 0;
}