网页资讯视频图片知道文库贴吧地图采购
进入贴吧全吧搜索

 
 
 
日一二三四五六
       
       
       
       
       
       

签到排名:今日本吧第个签到,

本吧因你更精彩,明天继续来努力!

本吧签到人数:0

一键签到
成为超级会员,使用一键签到
一键签到
本月漏签0次!
0
成为超级会员,赠送8张补签卡
如何使用?
点击日历上漏签日期,即可进行补签。
连续签到:天  累计签到:天
0
超级会员单次开通12个月以上,赠送连续签到卡3张
使用连续签到卡
03月11日漏签0天
c语言吧 关注:801,924贴子:4,376,833
  • 看贴

  • 图片

  • 吧主推荐

  • 视频

  • 游戏

  • 首页 上一页 1 2 3
  • 83回复贴,共3页
  • ,跳到 页  
<<返回c语言吧
>0< 加载中...

回复:从头看一遍《C和指针》,随手写一些经验和感悟吧

  • 只看楼主
  • 收藏

  • 回复
  • 我很淡定不稀奇
  • 彩虹面包
    13
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
感觉很有用,特别是对一些关键词用法与优劣解释,已经收藏,希望楼主继续更新。


  • ToFourier
  • 强能力者
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
左值和右值
虽然它的定义和C语言风格不太符合。左值就是可以出现在赋值号左边的东西,右值就是可以出现在赋值号右边的东西。
左值必须可以预料到存储结果的地址才能成为左值,而右值应该是个可以预料到它的值的东西。
隐式类型转换和算术转换
隐式类型转换:
C的整型运算总是至少以缺省整数类型的精度进行的。
char a,b,c;
…
a = b + c;
b和c的值被提升为缺省整型,再执行加法运算。运算结果是个缺省整形,会被截短,保留低8位,存储于a中。
算术转换:
若某个操作符的各操作数属于不同的类型,除非其中一个操作数转换为另一个操作数类型,否则操作无法进行。
寻常算术转换:
long double >double > float > unsigned long int > long int > unsigned int >int
如果某个操作数的类型排名较低,那么它首先转换为另一个操作数的类型然后执行操作。
较为常见的问题就是涉及到int和float运算的时候。比如:
float x;
int a = 2, b =4;
x = a / b;
你得到的数值肯定不是你想要的0.5。理想的方式是x = (float)a/b;或者将两个操作数全部转换为float类型。
还有一个问题就是计算结果溢出。比如
int a = 60000, b= 60000;
int c = a * b;
long d = a * b;
两种方法都不会得到正确结果。
a * b的结果是个int型数值。int c = a * b;时结果溢出。long d = a * b;时赋给c的值是一个由int转型为long的数据,相当于是long d = c;处理方式同上long d = (long)a * b;


2026-03-11 09:44:03
广告
不感兴趣
开通SVIP免广告
  • 追风筝的孩子
  • 路人
    2
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
楼主,怎么写一个较大一些的工程,我想写个东西,总是不知道该从哪里写起的感觉,我就只学了c语言,感觉学的还不错,我该从哪方面练起?


  • ToFourier
  • 强能力者
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
操作符的属性
操作符的优先级、操作符的结合性、操作符是否控制求值顺序决定表达式的求值顺序。
先执行优先级高的,优先级相同那么执行顺序由结合性(一串操作符是从左向右依次执行还是从右向左逐个执行)确定。还有4个操作符(&& || ?: ,),它们可以对整个表达式的求值顺序施加控制,它们要么保证某个子表达式能够在另一个子表达式的所有求值过程完成之前求值,或者使某个表达式跳过不再求值。


  • 挂uh
  • 便当
    3
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
求更,刚好可以复习复习😂


  • 莫遥临
  • 便当
    3
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
已收藏


  • ToFourier
  • 强能力者
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
指针
终于到指针了。讲实话,当初我学C语言的时候,对指针很是恐惧,尽量能不用就不用。但是指针终究还是有指针的好处的,不得不用。
6.1 内存和地址
计算机的内存由bit(位)组成,每个位只可能有两个值中的一种:0 或 1。
单个的位用处并不大,但是多个位组合起来,记住只是我们组合起来,计算机内存中仍然是按位存储的。
比如一般的,一个byte(字节)有8位,它可以存储无符号值0-255,或者有符号数-128~127.
可以把字节再组合起来。比如有的机器上以字(word)为单位存储整数,一般由2个字节或者4个字节表示。
但是,尽管一个word包含很多位,但它仍然只有一个地址,它的地址是最左还是最右字节的位置,取决于不同的机器。
总结起来两点:1. 内存中的每个位置由一个独一无二的地址标识。
2.内存中的每个位置都包含一个值。


  • ToFourier
  • 强能力者
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
6.2 值和类型
不能简单的通过检查一个值的位来判断它的类型。比如例子所举的一个32位值:
01100111011011000110111101100010.
书中以基于Motorola 68000处理器为例
类型 值
1个32位整数 1735159650
2个16位整数 26476和28514
4个字符 glob
浮点数 1.116533*10^24
可见,一个单一的值可以被解释为多种类型。所以,值的类型不是值本身所固有的特性,而是取决于它的使用方式。
6.3 指针变量
int a = 114, b =-1;
float c = 3.14;
int *d = &a;
float *e =&c;
这个其实就是上图的定义。
&操作符是取地址符。
变量d和e被声明为指针,并用其他变量的地址予以初始化。d和e的内容是地址,而不是a的整型值或者c的浮点值。d的内容和a的地址一致,e的内容和c的地址一致。要注意的是变量d也是有地址的,它的地址和它的内容一定要区分开。并不是说它的地址无用,后面会讲到还有一种指向指针的指针,就会取d的地址。
总结:变量的值就是分配给该变量内存位置所存储的值,即使是指针变量也不例外。


2026-03-11 09:38:03
广告
不感兴趣
开通SVIP免广告
  • ToFourier
  • 强能力者
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
6.4 间接访问操作符
通过一个指针访问它所指向的地址的过程称为间接访问。使用的是单目操作符*,这个和乘法的*写法一样,但是后者是双目操作符。二者优先级也不同。
表达式 右值 类型
a 112 int
b -1 int
c 3.14 float
d 100 int *
e 108 float *
*d 112 int
*e 3.14 float
d的值是100,对d使用间接访问时,表示访问内存位置100并查看那里的值。
6.5 未初始化和非法指针
程序运行中非法指针极大可能会导致内存访问异常等问题,从而程序异常报错甚至崩溃。
比如int *a; *a = 12;
可能发生的情况:1.你运气好的话,a的初始值是个非法地址,这时赋值语句会出错,从而终止程序;
2.要求整数必须存储于特定边界的机器而言,如果这种类型的数据在内存中的存储地址处在错误的边界上,对这个地址访问时会产生错误;
3.如果这个指针偶尔包含了一个合法地址,那么位于那个地址的值会被修改,虽然你没有打算修改他。这种错误很难发现。所以指针使用之前一定要初始化。
6.6 NULL指针
NULL指针表示某个特定的指针目前未指向任何东西。
不可以对NULL指针进行间接访问,这毫无意义。
在声明指针变量时,如果已知会被初始化为什么地址,就把它直接初始化为该地址,否则才初始化为NULL。并且在对指针进行解引用时,最后检查一下。比如if (p == NULL){}else{}


  • 小小冰雷师
  • 毛蛋
    1
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
新人顶顶


  • ToFourier
  • 强能力者
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
6.9 指针常量
需要通过地址访问内存中某个特定的位置。
比如假设变量a存储于位置100。
看看这句*100 = 25;猜猜有什么意义?看上去像是把25赋给存储于位置100的变量。可能你觉得这看着很别扭,那就对了,因为这么写本身就是错的。因为间接访问操作符只能作用于指针类型表达式。
正确的写法是*(int *)100 = 25;
我遇到的这么用的情况很少,为数不多的就是单片机开发的时候。正常其实也不会用到,但是会帮助你理解从硬件到软件的映射。
在stm32f103中普通的一句:
GPIOD
你可以一层层的跟进去看它是怎么定义的、
#defineGPIOD ((GPIO_TypeDef *)GPIOD_BASE)
#defineGPIOD_BASE (APB2PERIPH_BASE +0x1400)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#definePERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the aliasregion */
当我们访问GPIOD时,访问的其实是0x40000000+ 0x10000 + 0x1400位置。为什么是这个位置,可以查看对应的datasheet中的内存映射。


  • ToFourier
  • 强能力者
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
6.10 指针的指针
int a = 12;
int *b = &a;
int **c =&b;
*操作符具有从右向左的结合性,所以**c相当于*(*c).*c访问的是c所指向的位置,在这里是变量b,也就是**c相当于*b,最终的结果是访问变量a.
6.13 指针运算
指针加上一个整数的结果是另一个指针。问题是,指向哪里?
当一个指针和一个整数量相加时,整数在执行加法运算前会根据合适的大小进行调整。
如果将一个字符指针加1,运算结果产生的指针指向内存中的下一个字符。
假如float在机器上占据4个字节。那么当一个float型指针加1时,它会根据float类型的大小,这个加1会进行调整,最终实际加到float指针上的值为4.
同理,假如一个指向struct的指针加1,它实际加的是struct的大小。
6.13.1 算术运算
仅限于两种形式。一种是 指针+/- 整数,一种是 指针 – 指针。
指针+/- 整数: 标准定义这种形式只能用于指向数组中的某个元素的指针,且这类表达式的结果也是指针。但是也适用于malloc分配的内存。指针加1,指向下一个元素,减1,指向前一个元素。合理的做法是应该做边界测试,不要越界。
#define N 5
float values[N];
float *vp;
for(vp =&values[0]; vp < &values[N];)
*vp++ = 0;
这段代码是把此数组初始化为0.
指针 – 指针:只有当两个指针都指向同一数组中的元素时,才允许进行这种运算。两个指针相减的结果是一个有符号整型,它的值是两个指针在内存中的距离。但是你需要确保运算的结果在合法边界内。


  • ToFourier
  • 强能力者
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
7.1 函数定义/声明/
函数定义:即是函数体的实现。函数体是一个代码块,在函数被调用时执行。
函数声明:函数声明向编译器提供该函数的相关信息,以确保函数被正确调用。
函数定义的语法:
返回类型
函数名(形参)
代码块
说说返回类型吧。函数的返回值有什么用?
就我遇到的可以分为下面几种情况:
1. 计算类。比如求平均值,返回的是一个结果。除非使用出口参数,否则必带返回值。当然了你要是声明一个全局的变量,也可以不用返回,但是这样函数的内聚性不强,维护起来麻烦。
2. 功能类。你需要执行一个功能,比如打开一个串口,成功返回true,失败返回false。或者成功返回句柄,成功则返回可用句柄,否则INVALID_HANDLE_VALUE。这种情况下都是根据返回值的不同,进行不同的后续操作。打开成功了,继续执行。失败了,提示用户打开失败,检查串口或者重试等等。这个可以参考windows下的API,很多这种情况。
3. void。就是不返回,或者你根本不关心函数是否返回。值得注意的是就算是void返回类型,也可以直接用return;来结束函数体执行。
函数声明:
向编译器提供函数的某些信息,有两种方法实现。
1。比如在一个c文件中。你先定义了一个函数(注意是定义,不是声明),那么编译器就会记住它的参数数量和类型以及返回值类型。接着编译器就可以检查该函数的所有后续调用(注意是在同一源文件中)。
int fun(int a,int b)
{
return a+b;
}
void main()
{
int x = fun(2,3);
}
2.函数原型
int fun(int a,int b);
int fun(int ,int);
都可以作为fun函数的原型。
原型的好处就是你可以把它放在一个单独的头文件中,用#include包含该头文件,即可使用这个函数。


  • ToFourier
  • 强能力者
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
函数的参数:
C函数中所有参数均以“传值调用”方式进行传递。也就是说,你获得的是参数值的一份拷贝,在函数中所有的修改都是针对于这个拷贝值的。
常见的问题
void fun(int a)
{
a = a * a;
}
void main()
{
int a = 10;
fun(a);
printf(“a = %d\n”,a);
}
可能你最初的目的是要获取a的平方值,但是你会发现printf时,a的值仍然是10.
特别的,我们来看传入数组名或者指针时的情况。
当传入数组名时,在函数中使用下标引用修改数组中元素的值时,你会发现实际数组中的值真的被修改了。这种行为我们可以称之为“传址调用”。但是这和“传值调用”并不矛盾,因为实际上数组名是一个指针,传递给函数的是这个指针的拷贝,但在这拷贝上执行间接访问所访问的是原先数组。所以它只是在行为上像是通过传址调用。


登录百度账号

扫二维码下载贴吧客户端

下载贴吧APP
看高清直播、视频!
  • 贴吧页面意见反馈
  • 违规贴吧举报反馈通道
  • 贴吧违规信息处理公示
  • 首页 上一页 1 2 3
  • 83回复贴,共3页
  • ,跳到 页  
<<返回c语言吧
分享到:
©2026 Baidu贴吧协议|隐私政策|吧主制度|意见反馈|网络谣言警示