C程序设计语言-下

指针与数组

指针与地址

一元运算符&可用于取一个对象的地址。

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 = &num;
    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 等)。

使用位字段时需要注意的一点是,位字段的行为受限于具体的实现和编译器。例如,位字段的位数可能受到平台或编译器对字节对齐的规定影响。因此,在使用位字段时应当谨慎,并且需要确保对位字段的操作符合预期。

输入与输出

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值