C与指针——指针(一)

指针基础简述

6.1内存和地址

单个字节包含了存储一个字符所需要的位数,每个字节包含8位。
存储无符号值的范围:0 ~ 28-1(255)
存储有符号值的范围:-27(-128) ~ 27-1(127)

由四个字节存储的字,每个字包含4*8=32位
存储无符号值的范围:0 ~ 232-1(4294967295)
存储有符号值的范围:-231(-2147483648) ~ 231-1(2147483648)

高级语言通过名字而非地址来访问内存的位置;硬件仍然通过地址访问内存位置。

6.2 值、类型、指针变量与间接访问

不能简单地通过检查一个值的位来判断它的类型。
变量被声明为指针并不会改变这些表达式的求值方式,一个变量的值就是分配给这个变量的内存位置所存储的数值。
通过一个指针访问它所指向的地址的过程称为间接访问解引用指针(*)
间接访问操作只能作用于指针类型表达式。

因为对一个NULL指针使用解引用操作是非法的。因此在对指针进行解引用之前,首先确保它并非NULL指针。
为测试一个指针变量是否为NULL,可将它与零值进行比较。

间接访问操作符所需要的操作数是右值,但此操作符所产生的结果是左值。
指针变量可因其是变量而作为左值使用。对指针变量进行间接访问表示我们应该访问指针所指向的位置。

int a;
int *d=&a;//定义指针变量d并指向a

*d=10-*d;
//右侧对d所指向位置所存储的值(a的值)进行操作再将其赋给左侧d所指向的位置
d=10-*d;
//非法,表示把一个整型数量(10-d*)存储于一个指针变量(d)中。
    
*&a=25;//把值25赋值给变量a。&操作符产生变量a的地址(指针常量)
//*操作符访问其操作数(a的地址)所表示的地址
 
 p=&a;//把a的地址赋给指针变量p,指针变量p的值是变量a的地址,p指向a
*p=1;//将整数1赋给指针变量p当前所指向的变量,即a=1;

&—— 取地址运算符。&a是变量a的地址。
*—— 指针运算符(间接访问运算符),*p代表指针变量p指向的对象。

若偶尔需通过地址访问内存中某个的特定位置时(即用整型字面值的形式,左值为指针常量,右值为整型常量),可通过强制类型转换转换成适当的类型。

6.3 指针表达式

前缀自增++操作符优先级高于*,结合性都是从右向左。

表达式*cp++可作为左值和右值存在。(先执行++)
1.++操作符产生cp的一份拷贝
2.然后++操作符增加cp的值
3.最后,在cp的拷贝上执行间接访问操作。
表达式++*cp右值存在,左值非法。(先执行间接访问操作)
1.首先执行间接访问操作
2.cp所指向位置的值增加1
3.表达式的结果是这个增值后的值的一份拷贝
后缀自增(减)的优先级高于前缀自增(减)。
while((string=*strings++)!=NULL)
1.它把strings当前所指向的指针复制到变量string中
2.它增加strings的值,使它指向下一个值
3.它测试string是否为NULL

插播assert知识点:

断言assert是一个宏,该宏在<assert.h>中,,当使用assert时候,给他个参数,即一个判读为真的表达式。预处理器产生测试该断言的代码,如果断言不为真,则发出一个错误信息告诉断言是什么以及它失败一会,程序会终止。
我们一般可以用在判断某件操作是否成功上。
程序一般分为Debug版本和Release版本,Debug版本用于内部调试,Release版本发行给用户使用。
断言assert是仅在Debug版本起作用的宏,它用于检查“不应该”发生的情况。
assert不是一个仓促拼凑起来的宏,为了不在程序的Debug版本和Release版本引起差别,assert不应该产生任何副作用。所以assert不是函数,而是宏。程序员可以把assert看成一个在任何系统状态下都可以安全使用的无害测试手段。
以下是使用断言的几个原则:
1)使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况之间的区别,后者是必然存在的并且是一定要作出处理的。
2)使用断言对函数的参数进行确认。

在对指针执行间接访问前,指针必须进行初始化:使它指向现有的内存或给它分配动态内存。

编程练习

1.

请编写一个程序,它在一个字符串中进行搜索,查找所有在一个给定字符集合中出现的字符。这个函数的原型应该如下:

char *find_char(char const *source,char const *chars);

它的基本想法是查找source字符串中匹配chars字符串中任何字符的第1个字符,函数然后返回一个指向source中第1个匹配所找到的位置的指针。如果source中的所有字符均不匹配chars中的任何字符,函数就返回一个NULL指针。如果任何一个参数为NULL,或任何一个参数所指向的字符串为空,函数也返回一个NULL指针。
举个例子,假定source指向ABCDEF。如果chars指向XYZ、JURY或QQQQ,函数就返回一个NULL指针。如果chars指向XRCQEF,函数就返回一个指向source中C字符的指针。参数所指向的字符串是绝不会被修改的。
碰巧,C函数库中存在一个名为strpbrk的函数,它的功能几乎和这个你要编写的函数一模一样。但这个程序的目的是让你自己练习操纵指针,所以:
a.你不应该使用任何用于操纵字符串的库函数(如strcpy,strcmp,index等)。
b.函数中的任何地方都不应该使用下标引用。

分析: 这个函数是一个以指向两个字符型常量的指针变量作为形参且返回值为指针的函数。这个题目中的“函数返回一个指向source中第一个匹配所找到位置的指针”,这一句话也值得好好思考一下。题目中提到的strpbrk,是在源字符串(s1)中找出最先含有搜索字符串(s2)中任一字符的位置并返回,若找不到则返回空指针。头文件为#include <string.h>。

程序如下:

//方法一
#include <stdio.h>

char *find_char(char const *source,char const *chars);
//这里是指针变量作为函数参数,即将一个变量的地址传送到另一个函数中 
//用字符指针变量作形参和实参且函数返回指针值 
int main()
{
	char const  *s="ABCDEF"; 
	char const  *c="XRCQEF";
	char *t;

	printf("source=%s\nchars=%s\n",s,c);
    t=find_char(s,c);
	printf("Matched return value:%s",t);
	return 0;
}

char *find_char(char const *source,char const *chars)
{
    char *p;//设p为指向source中第一个匹配所找到位置的指针 
    if(source != NULL && chars != NULL){
        for(;*source != '\0';source++){
            for(p = chars; *p != '\0';p++)
                if(*p == *source)
                    return p;
        }
    }
    return NULL;
}

//方法二
#include<stdio.h>

char *find_char(char const *source,char const *chars);
int main(){
	char m[]="ABCDEF";
	char n[]="XRCQEF";
	char *t;
	char const *s=m; //使指针变量s指向m数组首元素
	char const *c=n; //使指针变量c指向n数组首元素
    printf("source=%s\nchars=%s\n",s,c);
	t=find_char(s,c);
	printf("Matched return value:%s",t);
	return 0;
} 

char *find_char(char const *source,char const *chars)
{
    char *ps = source;
    char *pc = NULL;
    if(source == NULL || chars == NULL)
        return NULL;
    while(*ps != '\0'){
        pc = chars;
        while(*pc != '\0'){
            if(*pc == *ps)
                return pc;
            pc++;
        }
        ps++;
    }
    return NULL;
}

//方法三
char *find_char(char const *source,char const *chars)
{
    int size = 0;
    if(source != NULL && chars != NULL){
        for(;*chars++ != '\0';size++)
            ;
        for(;*source != '\0';source++){
            for(chars-=size; *chars != '\0';chars++)
                if(*chars == *source)
                    return chars;
        }
    }
    return NULL;
}

运行结果:
在这里插入图片描述
注: 在运行时,纠结过返回的指针所指的究竟是source中还是chars中的字符串首地址,最后反复读题,根据题上所言的“指向source中的指针”可见,返回的指针不在source中,而应该是指向source中的,所以最后返回的指针所指的应该是chars中的字符串首地址。

虽然美名其曰是重新温习,但实际清楚的知道自己是来填坑的。之前有多浪,现在需要填的坑就有多深,尤其是在进行指针这一板块的内容时,深切体会到了大一的时候C语言老师语重心长提醒我们关于指针的知识点一定要重复多遍才能真正掌握的良苦用心。
关于指针的知识点,个人感觉,谭浩强的《C程序设计》中的讲解更为详细些,细节方面适合我这种小白阅读;而在《C和指针》中,只简单看完相关知识点,不参考其他的书籍,则在直接编程练习的过程中,做到一帆风顺是不容易的。
综上,这一章节的编程练习题,两本书混合食用效果更佳~

现在看着日历仿若是在看病危通知书
暑假自己定的计划终究也为证明人类的本质作了贡献
还有两天……(Death smiles)
好多人都说生活的迷人之处在于阴差阳错
可我不,我偏就是想要如愿以偿

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
指针是C编程语言中非常重要的概念之一,对于初学者来说可能会感到害怕和困惑。但只要理解了指针的原理和用法,就会发现它其实并不可怕。 首先,指针是一个存储变量地址的变量,它可以指向任何数据类型的变量,包括整型、浮点型、字符型等等。我们可以通过指针访问变量的值,也可以通过指针修改变量的值,这是指针的一大优势。 其次,理解指针的应用场景能够帮助我们更好地使用它。比如,当我们需要在函数之间传递大量的数据时,通过传递指针可以提高程序的执行效率。另外,在动态内存分配和释放中,指针也是一个必不可少的工具。 理解指针的用法也是很重要的。首先,我们需要理解指针的声明和初始化。指针的声明使用“类型 * 变量名”的语法,例如 int *ptr; 表示声明了一个指向整型变量的指针指针的初始化可以通过给指针赋值一个变量的地址或者通过取地址符&获取变量的地址。 然后,我们需要了解指针的运算。指针可以进行四种基本的运算:取地址运算符&,取值运算符*,指针加法和指针减法。取地址运算符&用于获取变量的地址,取值运算符*用于获取指针所指向的变量的值。指针加法和指针减法用于指针地址的增加和减少,不同数据类型的指针相加或相减会有不同的结果。 最后,我们需要注意指针的安全性。在使用指针的过程中,需要特别小心避免空指针、野指针等问题的出现,因为这些问题容易引发程序的崩溃或者产生不可预知的结果。 总结来说,指针作为C语言中的重要概念,我们应该尽早学习和掌握。只要理解指针的原理和用法,我们就能够更加灵活地操作内存,提高程序的效率和功能。通过不断的实践和学习,我们可以逐渐摆脱对指针的恐惧,让指针成为我们编程的得力助手。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值