1 指针与地址,指针变量
地址就是内存区中对每个字节的编号。如整型变量a、b、c,整型变量需要4个字节,分配的地址分别为1000、1004、1008。可以通过访问变量的地址来找到所需的变量,我们称变量的地址为变量的“指针”。上例中的1000、1004、1008分别为变量a、b、c的指针。
指针变量是指向一个变量的地址,将一个变量的地址值赋给一个指针变量后,这个指针变量就“指向”了该变量。在程序中用“*”符号表示指向。
1.1 指针变量的定义与赋值
定义:
类型说明 * 变量名
这里,变量名就是指针变量名,*表示这是一个指针变量,类型说明表示该指针变量所指向的变量的数据类型。
赋值:
& 变量名;
例:
int a;
int *p1 = &a;
int b;
int *p2;
p2 = &b;
指针变量在定义和引用的时候需要加“*”,定义后再赋值时不加“*”。
1.2 指针变量中的“&”与“*”
运算符 | 指针中的应用 |
---|---|
& | 地址运算符。取指定变量的地址。 |
* | 指针运行符。返回指定地址内变量的值 。 |
&* | 先*运算,再&运算。若变量p为变量a的指针,*p相当于变量a,&*p就相当于取变量a的地址。即,&*p与&a相同。 |
*& | 先&运算,再*运算。若变量p为变量a的指针,&a就是取变量a的地址,相当于p, *&a相当于变量a所在地址的值 ,实际就是变量a。即,*&a与*p相同。 |
& - 取地址;* - 取值
运算顺序:从右向左
1.3 指针的算术运算
指针只能做加减运算。
例1-指针自加:
#include <stdio.h>
int main()
{
int i=50, *p1=&i;
short j=20, *p2=&j;
printf("p1 = %d\n", p1);
p1++;
printf("p1++ = %d\n", p1);
printf("p2 = %d\n", p2);
p2++;
printf("p2++ = %d\n", p2);
return 0;
}
运行结果:
由上例可以看出,p1++和p2++不是指向下一存放基本整型数的地址,变量i是基本整型,所以在p1++后p1的值是变量i的地址加4(4个字节),变量j是短整型,所以p2++后p2的值是变量j的地址加2(2个字节)。
例2-指针交换:
#include <stdio.h>
int main()
{
int i, j, * p1, * p2, * t;
i = 50;
p1 = &i;
j = 20;
p2 = &j;
printf("Before -->\n");
printf("i = %d, j = %d\n", i, j);
printf("*p1 = %d, *p2 = %d\n", *p1, *p2);
t = p1;
p1 = p2;
p2 = t;
printf("After -->\n");
printf("i = %d, j = %d\n", i, j);
printf("*p1 = %d, *p2 = %d\n", *p1, *p2);
return 0;
}
运行结果:
由上例可以看出,指针的交换是交换指针指向的变量,原变量的值并未交换。p1原指向i,交换后指向j,p2原指向j,交换后指向i。
例3-指针实现数值运算
#include <stdio.h>
main()
{
int i=50, j=20, a, b, c, d, *p1=&i, *p2=&j;
a = *p1 + *p2;
b = *p1 - *p2;
c = *p1 * *p2;
d = *p1 / *p2;
printf("i = %d, j = %d\n", i, j);
printf("i+j=%d, i-j=%d, i*j=%d, i/j=%d\n", i+j, i-j, i*j, i/j);
printf("a=%d, b=%d, c=%d, d=%d\n", a,b,c,d);
}
运行结果
2 指针与数组
2.1 一维数组与指针
当定义一个一维数组时,系统会在内存中为该数组分配一个存储空间,这个数组的名字就是数组在内存的首地址。若定义一个指针变量,将数组的首地址传给变的指针变量,则该指针变量就指向了这个一维数组。以下两种定义是等价的:
int *p, a[10];
p = a;
int *p, a[10];
p = &a[0]
将数组名赋给指针变量时,不需要写“&”;
将数组首地址赋给指针时,需要写“&”。
例1-指针输出数组
#include <stdio.h>
main()
{
int a[10] = { 11,22,33,44,55,66,77,88,99,100 }, * p, i, n = 0;
/*printf("please input:\n");
for (i > 0; i < 10; i++)
scanf("%d", &a[i]);*/
p = a; //与p = &a[0]是等价的
printf("*p = %d\n", *p); //*p就是a[0]的值
printf("The even array is:\n");
for (i = 0; i < 10; i++)
{
if (*(p + i) % 2 == 0) //注:*(p+i)和*p+i是不一样的。若i=1, *(p+i)是a[1]的值(即22),*p+i是a[0]+1的值(即11+1=12)
{
printf("%5d ", *(p + i));
n++;
}
}
printf("\n");
printf("The total even number is: %d\n", n);
for (i = 0; i < 10; i++)
printf("%d: a[%d]=%d, *p+%d=%d, *(p+%d)=%d, *(a+%d)=%d\n", i, i, a[i], i, *p + i, i, *(p + i), i, *(a + i));
printf("The elements in array a are: ");
for (i = 0; i < 10; i++)
printf("%5d", *p++);
printf("\n");
}
运行结果:
由上例可以看出,
- p+i与a+i是等价的,都表示数组元素a[i]的地址
- *(p+i)与*(a+i)是等价的,都表示数组元素a[i]的值
- *p+i与*(p+i)是不同的,*p+i表示a[0]+i的值,*(p+i)表示a[i]的值
- p++表示指针p指向数组a的下一个元素的地址
例2-指针修改数组元素值
#include <stdio.h>
main()
{
int a[10] = { 11,22,33,44,55,66,77,88,99,100 }, * p, i;
p = a;
for (i = 0; i < 10; i++)
{
(*p)++;
p++;
}
for (i = 0; i < 10; i++)
printf("%5d", a[i]);
printf("\n");
}
运行结果
2.2 二维数组与指针
对于一个二维数组a[i][j],
- &a[0][0]:表示数组a第0行第0列的首地址,也是二维数组a的首地址;
- &a[m]:表示数组a的m行的首地址,也可用a+m表示;
- a[m]+n:表示数组a的m行n列的元素的地址;
- *(a+m)+n:表示数组a的m行n列的元素的地址;
- *(a+m):就是a[m];
- *(*(a+m)+n):表示数组a的m行n列元素,即a[m][n];
- *(a[m]+n):表示数组a的m行n列元素,即a[m][n];
m行首地址 | m行n列的元素地址 | m行n列的元素值 |
---|---|---|
&a[m] | &a[m][n] | a[m][n] |
a[m] | a[m] + n | *(a[m] + n) |
a+m 或 *(a+m) | *(a+m) + n | *(*(a+m) + n) |
若p为二维数组a[i][j]的指针,以下定义形式是等价的:
int a[2][3]={10, 20, 30, 40, 50, 60};
int* p = &a[0];
int a[2][3]={10, 20, 30, 40, 50, 60};
int* p = &a[0][0];
int a[2][3]={10, 20, 30, 40, 50, 60}, *p;
p = a[0];
int a[2][3]={10, 20, 30, 40, 50, 60}, *p;
p = a;
例子:
#include <stdio.h>
main()
{
int a[2][3]={10, 20, 30, 40, 50, 60}, *p;
p = a; //p指向了a[0]
printf("p:\t%d\n", p); //0行0列地址
printf("a:\t%d,\t*a:\t%d\n", a, *a); //0行首地址,0行0列元素地址
printf("a[0]:\t%d,\t*(a+0):\t%d\n", a[0], *(a+0)); //0行0列地址
printf("&a[0]:\t%d,\t&a[0][0]:\t%d\n", &a[0], &a[0][0]); //0行首地址,0行0列地址
printf("*p:\t%d,\ta[0][0]:\t%d\n", *p, a[0][0]); //0行0列元素值
printf("\n");
printf("&a[0][1]:\t%d,\t*a+1:\t%d\n", &a[0][1], *a+1); //0行1列地址
printf("p+1:\t%d,\t*(p+1):\t%d\n", p + 1, *(p + 1)); //0行1列地址,0行1列元素值
printf("\n");
printf("a[1]:\t%d,\ta+1:\t%d\n", a[1], a+1); //1行0列地址,1行首地址
printf("p+3:\t%d,\t*(p+3):\t%d\n", p+3, *(p+3)); //数组a的第3个元素地址,即1行0列地址;1行0列元素值
printf("&a[1][0]:\t%d,\t*(a+1)+0:\t%d\n", &a[1][0], *(a+1)+0); //1行0列地址
printf("\n");
printf("a[1]+2:\t%d,\t*(a+1)+2):\t%d\n", a[1]+2, *(a+1)+2);
printf("a[1][2]:\t%d,\t*(*(a+1)+2):\t%d\n", a[1][2], *(*(a+1)+2)); //1行2列元素值
printf("%d\n", a[1] + 0);
printf("\n");
printf("=======================\n");
printf("%d, %d, %d\n", &a[1], a[1], a + 1);
printf("%d, %d, %d\n", &a[1][2], a[1] + 2, *(a + 1) + 2);
printf("%d, %d, %d\n", a[1][2], *(a[1] + 2), *(*(a + 1) + 2));
printf("=======================\n");
printf("\n");
//分行输出数组
printf("The array a is:");
for (p = a[0]; p < a[0] + 6; p++)
{
if ((p - a[0]) % 3 == 0) printf("\n");
printf("%4d", *p);
}
printf("\n");
//输入位置,输出指定位置的元素值
int i, j, (*q)[3];
q = a;
p = a[0];
printf("Please input a position (e.g. i=0,j=0):\n");
scanf("i=%d,j=%d", &i, &j);
printf("a[%d][%d] = %d\n", i, j, *(*(q + i) + j));
printf("a[%d][%d] = %d\n", i, j, *(p + (i*3+j)));
printf("a[%d][%d] = %d\n", i, j, *(*(a + i) + j));
}
运行结果:
此例中,(*q)[3]是定义一个指向包含3个元素的一维数组的指针变量,也就是q所指的对象是有3个元素的数组,它的值是该一维数据的首地址。可以将q看成是二维数组的行指针,q+i表示二维数组第i行的地址。*(*(q + i) + j))表示二维数组i行j列的值,即a[i][j]的值,与*(*(a + i) + j))等价。
3 指针与字符串
3.1 字符型指针
字符型指针就是指向字符型内存空间的指针变量。使用字符型指针可以访问字符串。
一般形式:
char *p;
例如:
main()
{
char *p = "Hello world!", *q;
printf("%s\n", p);
q = "Yeah!";
printf("%s\n", q);
char str1[] = "I have a dream.", str2[50], *p1, *p2;
p1 = str1;
p2 = str2;
printf("*p1 = %c\n", *p1); //此时p1的指针指向str1的第一个字符
printf("p1 = %s, str1 = %s\n", p1, str1); //输出整个字符串
//将str1中的内容复制到str2中
while (*p1 != '\0')
{
*p2 = *p1; //一个字符一个字符赋值
p1++; //p1指针指向str1的下一个字符地址
p2++; //p2指针指向str2的下一个字符地址
}
*p2 = '\0'; //向str2末尾写入终止符\0
printf("%s\n", str1);
printf("%s\n", str2);
//此时p1和p2均指向字符串的末尾,因此无法用p1和p2来输出str1和str2的值
}
运行结果:
3.2 字符串数组
对于字符串数组,可以用指针数组来访问。当一个数组,它的元素均为指针类型数据,那么这个数组就是指针数组。指针数组中的每个元素都相当于一个指针变量。
例1-输出字符串数组
#include <stdio.h>
#include <string.h>
main()
{
char* Nums[] = { "one", "two", "three", "four", "five", "six", "seven"};
int Nums_len = sizeof(Nums) / sizeof(Nums[0]);
for (int i = 0; i < Nums_len; i++)
printf("%s\n", Nums[i]);
}
运行结果:
例2-字符串数组应用
#include <stdio.h>
#include <string.h>
iSort(char* iStrings[])
{
char* temp;
int i, j;
int ilength = sizeof(iStrings) / sizeof(iStrings[0]);
for (i = 0; i < ilength; i++)
{
for (j = i + 1; j < ilength; j++)
{
if (strcmp(iStrings[i], iStrings[j]) > 0)
{
temp = iStrings[i];
iStrings[i] = iStrings[j];
iStrings[j] = temp;
}
}
}
}
int iMatch(char* matchstr, char* istring)
{
int istring_len = strlen(istring);
int matchstr_len = strlen(matchstr);
if (istring_len < matchstr_len)
return -1;
int i, j;
for (i = 0; i < istring_len; i++)
{
if ((istring_len - i) < matchstr_len)
return -1;
if (istring[i] == matchstr[0])
{
int m = 0;
for (j = 0; j < matchstr_len; j++)
{
if (istring[i + j] != matchstr[j])
{
m = -1;
break;
}
}
if (m == 0)
return i;
}
}
return -1;
}
main()
{
//排列字符串,按字母顺序输出
char* iStrings[] = { "One", "Two", "Three", "Four", "Five" };
iSort(iStrings);
int len = sizeof(iStrings) / sizeof(iStrings[0]);
for (int i = 0; i < len; i++)
printf("%s\n", iStrings[i]);
printf("\n");
//匹配字符串
char contents[] = "Hello World! One world, one dream!";
printf("%s\n", contents);
char* matchstrs[] = { "dream", "world", "and", "World", "Yeah" };
int matchstrs_len = sizeof(matchstrs) / sizeof(matchstrs[0]);
int matchindex = -1;
for (int j = 0; j < matchstrs_len; j++)
{
matchindex = iMatch(matchstrs[j], contents);
if (matchindex != -1)
{
printf("%s: Matchable!, match index = %d\n", matchstrs[j], matchindex);
}
else
printf("%s: Unmatchable!\n", matchstrs[j]);
}
}
运行结果:
4 指针作函数参数
4.1 指针变量作函数的参数
指针型变量可以作为函数的参数。使用指针作为函数的参数是将函数的参数声明为一个指针。
- 当数组作为函数的实参时,传递的是数组的地址;
- 当用数组名作为函数的实参时,传递的是指向该数组的第一个元素的指针。
4.2 数组指针作函数的参数
- 形参和实参均为指针变量;
- 数组名就是这个数组的首地址,所以可将数组名作为实参传给形参;
- 当形参为数组时,实参也可以为指针变量。
例1-指针变量作函数的参数
#include <stdio.h>
iSwap_num(int* a, int* b)
{
int tmp; //tmp为整型变量
tmp = *a; //指针a指向的地址空间的值赋给整型变量tmp
*a = *b; //指针b指向的地址空间的值赋到指针a指向的地址空间中
*b = tmp; //整型变量tmp的值值赋到指针b指向的地址空间中
//到这里,指针a和指针b所指向的地址空间的内容已经对调
}
iSort_num(int* a, int* b, int* c)
{
//从小到大排序
if (*a > *b)
iSwap_num(a, b);
if (*a > *c)
iSwap_num(a, c);
if (*b > *c)
iSwap_num(b, c);
}
main()
{
int x, y, z, * p1, * p2, * p3;
p1 = &x;
p2 = &y;
p3 = &z;
printf("Please input three numbers (e.g. 0,1,2):\n");
scanf("%d,%d,%d", p1, p2, p3);
iSort_num(p1, p2, p3);
printf("After sorted from small to large:\n");
printf("%d,%d,%d\n", x, y, z);
}
运行结果
此例可以看出变量x,y,z的值最后发生了变化。
例2-一维数组使用指针变量作函数参数
#include <stdio.h>
iOrder_array(int* p, int array_length)
{
//从小到大排序
int i, j, t;
for (i = 0; i < array_length - 1; i++)
{
for (j = 0; j < array_length - 1 - i; j++)
{
if (*(p + j) > *(p + j + 1))
{
t = *(p + j);
*(p + j) = *(p + j + 1);
*(p + j + 1) = t;
}
}
}
printf("After sorted, the array is:\n");
for (i = 0; i < array_length; i++)
{
if (i % 4 == 0)
printf("\n");
printf("%5d", *(p + i));
}
printf("\n");
}
int main()
{
int iArray[50], i, n;
puts("Please input the array element's count (less than 50):\n");
scanf("%d", &n);
if (n > 50)
{
printf("Error count input - %d, should less than 50.", n);
return -1;
}
//方法一:数组变量为函数实参,指针变量为函数形参
puts("Please input the array elements:\n");
for (i = 0; i < n; i++)
scanf("%d", iArray+i);
iOrder_array(iArray, n);
//方法二:指针变量为函数实参,指针变量为函数形参
int* p;
p = iArray;
puts("Please input the array elements:\n");
for (i = 0; i < n; i++)
scanf("%d", p++);
p = iArray;
iOrder_array(p, n);
}
运行结果
例3-二维数组使用指针变量作函数参数
#include <stdio.h>
iLine_max(int (*a)[5])
{
int max_value, i, j;
for (i = 0; i < 4; i++)
{
max_value = *(*(a + i));
for (j = 0; j < 5; j++)
{
if (*(*(a + i) + j) > max_value)
max_value = *(*(a + i) + j);
}
printf("%d line: max value - %d\n", i, max_value);
}
}
main()
{
int a[4][5] = {
11, 99, 10, 30, 51,
22, 88, 90, 70, 52,
33, 77, 20, 40, 53,
44, 66, 80, 50, 54,
};
int (*p)[5];
p = a;
iLine_max(p);
}
运行结果
5 指向指针的指针
指向指针类型变量的指针变量称为指向指针的指针变量。
类型说明 **指针变量名
如:
int **p;
此处**p相当于*(*p)。
例子:
#include <stdio.h>
main()
{
int a[6] = { 11,22,33,44,55,66 }, * p1, ** p2;
p1 = a;
p2 = &p1;
for (int i = 0; i < 6; i++)
{
printf("%5d", *(p1 + i));
printf("%5d", *(*p2 + i));
printf("\n");
}
char* Count[] = {
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten"
};
char** p;
int j;
p = Count;
for (j = 0; j < 10; j++)
{
printf("%s\n", *(p + j));
}
}
运行结果:
6 函数型指针
指针变量也可以指一个函数。返回指针值的函数称为指针函数。
指针函数定义:
类型说明 *函数名(参数列表)
例子:
#include <stdio.h>
char* iWord(char x)
{
switch (x)
{
case 'A': return "One";
case 'B': return "Two";
case 'C': return "Three";
case 'D': return "Four";
case 'E': return "Five";
case 'F': return "Six";
case 'G': return "Seven";
case 'H': return "Eight";
case 'I': return "Nine";
case 'J': return "Ten";
default: return "None";
}
}
main()
{
char x, *y;
printf("Please input a upper letter:\n");
scanf("%c", &x);
y = iWord(x);
printf("%s\n", y);
printf("%s\n", iWord(x));
}
运行结果
7 main函数的参数
main函数是主函数,是所有程序的入口。main函数可以是有参函数。定义格式为:
main(int argc, char *argv[])
{
...
}
当源程序经过编译、链接后,会生一个扩展名为.exe的可执行文件,可以在操作系统下直接运行。当main函数为无参函数时,操作系统中命令行的命令为:
.exe文件名
当main函数为有参函数时,操作系统中命令行的命令为:
.exe文件名 参数1 参数2 参数n
例子
#include <stdio.h>
main(int argc, char* argv[])
{
printf("The list of parameter ==>\n");
printf("command: %s\n", *argv);
printf("parameter number: %d\n", argc);
for (int i = 0; i < argc; i++)
printf("argv[%d] = %s\n", i, argv[i]);
}
运行结果
D:\VisualStudio_workspace\projects\test\x64\Debug>test.exe
The list of parameter ==>
command: test.exe
parameter number: 1
argv[0] = test.exe
D:\VisualStudio_workspace\projects\test\x64\Debug>test.exe red green yellow blue dark white
The list of parameter ==>
command: test.exe
parameter number: 7
argv[0] = test.exe
argv[1] = red
argv[2] = green
argv[3] = yellow
argv[4] = blue
argv[5] = dark
argv[6] = white
此运行结果为系统命令行中执行exe文件的结果,为两次exe文件的执行。第一次执行exe文件,命令中没有加参数;第二次执行exe文件,命令中加有参数。
标签:p2,p1,int,基础,C语言,++,printf,指针 From: https://blog.csdn.net/weixin_46891908/article/details/142041329