指针与数组
指针与地址
一元运算符&可用于取一个对象的地址。
p = &c;
一元运算符*是间接寻址或间接引用运算符。当它作用域指针时,将访问指针所指向的对象。
#include<stdio.h>
int main()
{
int x = 1;
int y = 2;
int z[10];
int *ip;
ip = &x;
printf("%d\n",*ip);
*ip = 0;
printf("%d\n",*ip);
}
我们应该注意,指针只能指向某种特定类型的对象。(一个例外情况是指向void类型的指针)
一元运算符*和&优先级比算术运算符的优先级高。因此下面条语句。
int y = *ip + 1;
还能这么做
*ip += 1;//其值增加1
*ip = ++*ip;;
*ip = (*ip)++;
语句(ip)++中的圆括号是必需的。否则,该表达式将对ip进行加1运算而不是对ip指向的对象进行加1运算。这是因为类似于*
和++这样的一元运算符遵循从右到左的结合顺序。
指针也是变量,所以在程序中可以直接使用,而不必通过间接引用的方法使用,如果iq是另一个指向整型的指针。俺么语句
iq = ip;
将把ip中的值拷贝到iq中。iq也指向ip的对象。
int p = &a; 和 int p; p = &a; 等价的原因并不是因为优先级的问题,而是因为 C 语言中的声明语法和赋值语法的规定。
在 C 语言中,变量的声明语法是 <类型> <标识符>,例如 int a; 表示声明一个 int 类型的变量 a。而指针变量的声明语法是 <类型> *<标识符>,例如 int *p; 表示声明一个指向 int 类型的指针变量 p。
所以,int *p = &a; 中的 int *p 部分是指针变量的声明语法,而 = &a; 部分是对指针变量 p 进行初始化的赋值语法。这种写法是将声明和初始化合并到一行中。
而 int* p; p = &a; 中的 int* p 是指针变量的声明语法,而 p = &a; 是对指针变量 p 进行赋值的操作,将其指向变量 a 的地址。
在这两种写法中,都能正确地声明指针变量 p 并将其指向变量 a 的地址,因此它们是等价的。这是 C 语言的语法规定,并不涉及到优先级的问题。
#include<stdio.h>
int main()
{
int a =100;
int *p = &a;
printf("a == %d,a == %p\n",a,&a);
printf("*p == %d,*p == %p\n",*p,&*p);
printf("p == %d,p == %p\n",p,&p);
int *q;
q = &a;
printf("a == %d,a == %p\n",a,&a);
printf("*q == %d,*q == %p\n",*q,&*q);
printf("q == %d,q == %p\n",q,&q);
return 0;
}
指针与函数参数
比如函数里面传参,在函数里调换参数的数值,不会在主函数里更改其数值。
那么可以使用指针,来实现改变主函数变量的数值。
#include<stdio.h>
void swap(int *x,int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
int main()
{
int x = 1;
int y = 2;
swap(&x,&y);
printf("%d %d",x,y);
}
指针与数组
在C语言中,指针和数组之间的关系十分密切。
int *pa;
//下面两条语句是相等的
pa = &a[0];
pa = a;
对数组元素a[i]的引用也可以写成*(a+i)。
如果pa是一个指针相应的也可以写成pa[i];
int b = *(a+2);
int c = pa[2];
但是务必要记住的是数组名和指针之间有一个不同之处,指针是一个变量,因此C语言中pa = a和pa++都是合法的。但是数组不行!
#include<stdio.h>
void strlen(char *x)
{
int i;
for(i = 0;*x != '\0';x++,i++){
}
printf("%d\n",i);
}
int main()
{
char x[50] = "你好!";
strlen(x);//输出5
strlen(x + 2);//输出3
strlen("我非常好谢谢!");
char s[50] = "嗯嗯!";
char *ss = s;
strlen(ss);
}
地址算术运算
#include<stdio.h>
#define ALLOCSIZE 1000
static char allocbuf[ALLOCSIZE];
static char *allocp = allocbuf;
char * alloc(int n)
{
if(allocbuf + ALLOCSIZE - allocp >= n)
{
allocp += n;
return allocp - n;
}else
return 0;
}
void afress(char *p)
{
if(p>= allocbuf && p < allocbuf + ALLOCSIZE)
allocp = p;
}
字符指针与函数
字符串常量是一个字符串数组。
"i am a string"
在字符串内部表示中,字符串数组一空字符’\0’结尾。所以程序可以通过检查空字符找到字符数组的结尾。字符串常量占据的存储单元数也因此比双引号内的字符数大1。
把一个指针该字符数组的指针赋值给pmessage
char *pmessage;
pmessage = "now is the time";
#include<stdio.h>
main()
{
char *pmessage;
pmessage = "now is the time";
printf("%c\n",*pmessage);
printf("%s\n",++pmessage);
printf("%c\n",*pmessage++);
printf("%c\n",*pmessage++);
printf("\n");
char A[50] = "nowis the time";
char *qmessage = A;
printf("%c\n",*qmessage);
printf("%s\n",qmessage);
printf("%s\n",++qmessage);
printf("%c\n",* ++ qmessage);
printf("%c\n",* ++ qmessage);
}
练习 5-3
#include<stdio.h>
//练习 5-3
//设计一个字符串拼接
void strcat(char *S,char *Q)
{
char *z = S;
for(; *S!= '\0';*S++)
{
printf("%c",*S);
}
printf("%\n");
for(;*Q!='\0';*Q++,*S++)
{
*S = *Q;
}
printf("拼接后: \n");
for(; *z!= '\0';*z++)
{
printf("%c",*z);
}
}
main()
{
char S[] = "ABCDEFG";
char Q[] = "EEEEEEE";
strcat(S,Q);
}
指针数组以及指向指针的指针
当我们谈到指针数组时,我们指的是一个数组,其中的每个元素都是指针。换句话说,指针数组是一个存储指针的数组。
下面是一个指针数组的示例:
#include <stdio.h>
int main() {
int num1 = 10, num2 = 20, num3 = 30;
int *ptrArr[3]; // 声明一个包含3个指针的指针数组
ptrArr[0] = &num1; // 存储num1的地址
ptrArr[1] = &num2; // 存储num2的地址
ptrArr[2] = &num3; // 存储num3的地址
// 使用指针数组访问各个变量
printf("Value of num1: %d\n", *ptrArr[0]);
printf("Value of num2: %d\n", *ptrArr[1]);
printf("Value of num3: %d\n", *ptrArr[2]);
return 0;
}
在上面的示例中,我们声明了一个名为 ptrArr 的指针数组,它包含了3个指针。然后,我们将 num1、num2 和 num3 的地址存储到 ptrArr 数组的不同元素中。最后,我们使用指针数组来访问并打印各个变量的值。
指向指针的指针,也称为二级指针,是指一个指针变量存储了另一个指针变量的地址。通过使用指向指针的指针,我们可以间接地访问指针所指向的值。
下面是一个指向指针的指针的示例:
#include <stdio.h>
int main() {
int num = 42;
int *ptr = #
int **ptrToPtr = &ptr; // 指向指针的指针
// 使用指向指针的指针间接访问变量的值
printf("Value of num: %d\n", **ptrToPtr);
return 0;
}
多维数组
#include <stdio.h>
int main() {
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 访问二维数组中的元素
printf("Value at matrix[0][2]: %d\n", matrix[0][2]);
printf("Value at matrix[1][3]: %d\n", matrix[1][3]);
return 0;
}
指针数组的初始化
char *A[] = {"A","B","C"};
char A[][15]={"A","B","C"};
命令行参数
调用主函数main时,它带有两个参数,第一个参数(习惯上称为argc,用于参数计数)的值表示运行程序时命令行中参数的数目;第二个参数(称为argv,用于参数向量)。
按照C语言的约定,argv[0]的值是启动该程序的程序名,因此argc的值为至少为1.如果argc的值为1,则说明程序名后面没有命令行参数。
#include<stdio.h>
int main(int argc,char *argv[])
{
int i;
for (i = 0;i<argc;i++)
{
printf("%s%s",argv[i],(i < argc - 1)? " ": "");
}
printf("\n");
return 0;
}
//C:\Users\Administrator\Desktop\5.6\C程序设计语言\练习5-3.exe
根据你提供的代码,假设你在命令行中运行的是 练习5-3.exe 可执行文件。在命令行中,你可以输入参数来传递给该可执行文件。在这种情况下,argc 参数表示命令行参数的数量,argv 参数是一个指向参数字符串的指针数组。
对于 C:\Users\Administrator\Desktop\5.6\C程序设计语言\练习5-3.exe 这个路径,它被作为第一个参数传递给可执行文件。因此,在输出结果中,argv[0] 的值就是这个路径。
请注意,argv[0] 通常表示可执行文件本身的路径或名称。如果你想要访问命令行传递给可执行文件的其他参数,你需要从 argv[1] 开始。
如果你在命令行中使用类似以下的命令运行可执行文件:
练习5-3.exe Hello World!
练习5-3.exe Hello World!
其中 argv[0] 为 练习5-3.exe,argv[1] 为 Hello,argv[2] 为 World!。
指向函数的指针
#include <stdio.h>
// 函数原型
int add(int a, int b);
int main() {
// 声明一个指向函数的指针
int (*ptr)(int, int);
// 将函数地址赋给指针变量
ptr = add;
// 使用指针调用函数
int result = ptr(3, 4);
printf("Result: %d\n", result);
return 0;
}
// 定义一个函数
int add(int a, int b) {
return a + b;
}
结构
struct point origin,*pp;
pp = &origin;
printf("origin is (%d,%d)\n",(*pp.x),(*pp.y));//一定要有括号因为.的优先级比*高
printf("origin is (%d,%d)\n",pp->x,pp->y);
struct {
int len;
char *str;
}*p;
++*p->len;
//增加的是len的值,而不是p的值。其中由隐形括号关系++(p->len)。
//同样的道理*p->str读取的是str所指向对象的值。*p->str++先读取指针str指向的对象的值,再将str加1.*p++->str先读取指针str指向的对象的值,然后再将p加1。
为字段
struct
{
int is_keyword:1;
int is_extern:1;
}flasgs;
这段代码定义了一个名为 flags 的结构体变量,并且该结构体包含了两个字段:is_keyword 和 is_extern。每个字段都是一个整数类型,但在这个结构体中,它们被声明为只占用1位的位字段(bit-field)。
位字段允许我们以更紧凑的方式存储数据,使用少量的位来表示特定的标志或状态。在这个例子中,每个字段都只需要1位来表示相应的标志(is_keyword 和 is_extern)。
这里使用了冒号 : 后面跟着数字 1,表示每个字段所占用的位数。通过限制字段的位数,我们可以有效地节省内存空间。
结构体中的位字段可以具有不同的位数,也可以使用其他整数类型(如 char、short、int 等)。
使用位字段时需要注意的一点是,位字段的行为受限于具体的实现和编译器。例如,位字段的位数可能受到平台或编译器对字节对齐的规定影响。因此,在使用位字段时应当谨慎,并且需要确保对位字段的操作符合预期。