《C与指针》数据&语句&操作符表达式

了解&基本概念

  • 我这本书的C版本是C89。
  • 要从逻辑上删除一段C代码,更好的方法是使用 #if 指令。
#if 0
    statements
#endif
  • 引用:传址调用,也就是数组参数。标量和常量则是按值传递的。
  • C中约定,字符串就是一串以 NUL 字节结尾的字符。NUL作为字符串终止符,并不被看作整个字符串的一部分。
  • NUL和NULL

        NUL = ‘\0’,NULL是指一个其值为0的指针。都是整型值,值是相同的。但是所代表的意义不同,因此需要在合适的场合使用合适的常量。

        NUL需要自行定义,NULL是头文件stdio中定义的。

  • scanf 函数的返回值是函数成功转换并存储于参数中的值的个数。
  • EOF表示文件的结尾
  • C可以把赋值操作蕴含于while语句内部,这样就允许程序员消除冗余语句。
  • 一个函数的形参变量被声明为const

        在这个函数中,这个参量不能被修改。

        编译器会自动去验证这个意图。

function(const int value){
    statement
}

当const出现如上的情况时,在程序运行时,function函数中传入的是参数的值(数值),并不像正常的常量一样保存在常量区内。

  • 使用数组的时候,一定要注意数组的溢出

比如在条件判断语句中用&&加上溢出判断

3.数据

  • 变量的三个属性:作用域;链接属性;存储类型

这三个属性决定变量的“可视性”(变量可以在什么地方使用),“生命期”(变量的值能够保存多久)。

基本数据类型:整型、浮点、指针、聚合类型(结构体和数组)

整型

C文件:limits.h

整型的类型和范围

C89引入了新标准,也就是各个整型值的最小范围,这种方式有助于C程序在不同的平台之间的移植。

类型最小范围
char0~127
signed char-127~127
unsigned char0~255
short int-32767~32767
unsigned short int0~65536
int-32767~32767
unsigned int0~65536
long int-2147483647~2147483647
unsigned long int0~4294967295

必须满足最小范围,short整型最少16位,long整型最小32位,至于int需要看编译器的规定。

最大范围并没有限制,因此也有三个整型都是同样大小,例如long、short和int都是32位的情况,这些都要依据使用的编译器和平台。

对于char类型来说,不同的编译器可能会分配给char不同的大小,有的是有符号,有的是无符号。

这就会产生可移植问题,统一将char声明为unsigned或者signed都不是很好的方式。

当可移植问题比较重要的时候,我们需要将char变量的范围限制在有符号和无符号之间,例如ASCII的字符都是在这个范围内的。

整型字面值

  • 整型字面值就是程序中出现的各种直接数值,例如1,0,300这类的数字,这些数字指定了自身的数值,并且无法改变。
  • 可以在整形字面值的后面添加后缀来指定其整型类型,例如后缀L就是long,后缀u就是unsigned。
  • 添加前缀能够标注整型字面值的数制类型,也就是八进制、十进制、十六进制。

        八进制以0开头,十六进制以0x开头。       

字符常量

字符常量有一个前缀L时,表示的是宽字符常量。宽字符常量不是很理解,网上说是用于编码之间的转换,unicode和ascii这两个码,微软关于C宽字符常量的使用方式如下。

以前缀开头的字符文本 L 是宽字符文本。

包含单个字符、转义序列或通用字符名称的宽字符文本的值的值 等于其编码在执行宽字符集中的数值,除非字符文本在执行宽字符集中没有表示形式,在这种情况下,该值是实现定义的。

包含多个字符、转义序列或通用字符名称的宽字符文本的值是实现定义的。 有关 MSVC,请参阅下面的Microsoft 特定部分。

👇

宽字符串文本是一个以 null 结尾的常数数组,其 wchar_t 前缀为 " L ",包含除双引号( " )、反斜杠( \ )或换行符以外的任何图形字符。

宽字符串文本可包含上面列出的转义序列和任何通用字符名称。

枚举类型

枚举默认按顺序赋值,这些都是整型值,第一个的值为0,第二个是1,以此类推。

字符串常量

  • 在C中没有字符串的概念,一串字符串的本质是以NUL字节结尾的零个或多个字符。
  • 字符串通常存储在字符数组里面
  • 字符串都是以NUL结尾的,NUL并不是一个可打印的字符。

typedef

  • C语言支持typedef这个机制,可以为各种数据类型定义新名字。
  • #define和typedef是不一样的,在使用的时候应当注意,#define是一种预处理命令,在预处理阶段将#define的内容进行替代,无法合理处理数据类型。

常量

定义常量有两种形式:const关键字和#define。

  • const将一个变量变成“不能被修改”的变量,被用于只允许使用变量的地方
  • #define是一种创建名字常量的机制,用于具有特殊含义的整形字面值。

变量的三个属性:链接属性、作用域、存储类型

作用域

程序中标识符能够被使用的区域,作用域要看变量的声明位置。那么在该变量的作用域之外的同名变量,并不影响该变量的使用。

  • 代码块作用域

        在代码块内部声明的变量,仅限于在代码块内部使用,作用域就是从声明处开始,代码块结束位置结束。

  • 文件作用域

声明在所有代码块之外的标识符具有文件作用域,从文件开始到文件结束都能够被访问。

  • 原型作用域

适用于在函数里声明的参数,作用范围在这个函数内。

  • 函数作用域

链接属性

处理在不同文件中出现的标识符,注意,链接属性和作用域有关,但并不等于作用域。

external(外部):无论标识符声明多少次,位于几个源文件,都表示一个实体。

internal(内部):在同一个文件中,无论标识符声明多少次,都表示一个实体。

none(无):被作为一个单独的实体,每次声明都指向不同的实体。

  • 关键字extern和static用于修改标识符的链接属性。extern能将internal转换成external,static能将external转换成internal。
  • 文件内的全局变量的链接属性为internal,同理代码块的变量也是这样。作用域只有自己的区域。
  • 函数的链接属性为external,因此外部是可以调用的。
  • 链接属性标识符是一次性的,第一次声明之后,后面第二次以及以上都不修改第一次的链接属性。

存储类型

存储变量值的内存类型,决定变量在内存中何时创建、何时销毁以及它的值保存多久。

  • 普通内存
  • 运行时堆栈
  • 硬件寄存器

详细整理👉[程序运行以及内存管理]

关键字:auto、static、register

代码块内部声明的变量,存储在堆栈之中,称为自动变量,关键字auto。

static:

        函数定义:用于修改标识符的链接属性,但是标识符的存储类型和作用域不受影响

        代码块内部的变量声明:修改变量的存储类型,但是变量的链接属性和作用域不受影响。

这三个属性描述一个变量在代码内、可执行文件内、内存内的一些情况。作用域用于描述该变量使用范围,链接属性用于说明变量与其他文件中标识符的关系,存储类型用于说明变量被保存的地方。

前面说过,变量的这三个属性决定变量的可视性和生命期。

👉在不同文件中具有相同名称的变量,是同一个变量么?

在变量的声明代码块内,作用域以及变量的链接属性。

首先要看这两个变量的声明,来确定变量的三个属性。声明的位置确定变量的作用区域,链接属性确定能否在不同的文件中使用,变量的类型确定在内存中占据的大小。这三者共同决定变量在内存中的位置(堆栈、静态存储区)。

根据这三个属性,就能知道它的作用范围和生命周期。

4.语句

空语句:只包含一个分号。用于语法要求出现一条完整的语句,但并不需要做执行任何人物的时候。

表达式语句:C中没有“赋值语句”,而是将赋值操作符结合表达式,给变量赋值。赋值就像是加法减法一样,是一种操作。

赋值语句会利用函数的‘副作用’。

一个函数执行主要的功能,同时会产生一些其他的影响,这些其他的东西就是副作用。

例如 printf 打印字符串的时候,会返回一些校验值,但是我们只关心它打印字符串的功能,并不关心其返回值。

但是对于getchar()这个函数,主要的功能是读取一个输入的字符串,如果我们没有去读取getchar的返回值,那么getchar读取到字符后就丢弃它。

  • 判断语句 if 中要注意,C并不具备布尔类型,而是用整型来代替。因此 if 语句判断的时候,0值表示假,非零值表示真。如果你想准确的判断真假,应该用更加精确的0和1来判断。
  • while循环中,判断条件可以加在 if 语句中,也可以用while来判断,要注意条件一定要正确。
  • for语句包含初始化、条件和调整部分。continue关键字直接将本轮循环转换到调整部分。
  • switch语句有一些限制:

switch后面判定的值一定要是整型

case标签所具有的值一定要是常量

如果case没有break,那么执行流将贯穿整个switch代码块。

实际上,你可以根据实际需要选择是否让switch执行流贯穿代码块,但是这种情况十分罕见。

C不具备任何输入输出语句,是通过IO调用库函数来实现的。

C也不具备任何异常处理语句,它们也是通过调用库函数来实现的。

5.操作符和表达式

(按照操作符的优先级顺序)

  • 算数运算符中,除%之外,其他的运算符既适用于整数又适用于浮点
  • 移位操作符中,右移位操作面临一个补充的问题:逻辑移位和算数移位。逻辑移位就是简单直白的用0填充右移过程中的空位。算术移位使用符号位填充空位,不改变数值的正负。
  • 对于运算符的使用,需要参考你用的编译器。
  • 赋值运算符返回的值是左操作数的新值。

y = (x = 4+5):这里,x为9,同时(x = 4+5)返回的值为9,因此 y 也等于9。

  • 逻辑运算符:利用操作符优先级进行“短路求值”,可以节省时间,经典例子->Pascal。
  • 逗号操作符,用于将多个表达式分开,从左向右逐个求值。一整个逗号表达式的值为最后的一个表达式的值。
  • 编写C程序的时候,要注意语句的执行顺序和逻辑。

参考资料:

《C与指针》

https://www.cnblogs.com/gatsby123/p/11150472.html

https://docs.microsoft.com/zh-cn/cpp/cpp/string-and-character-literals-cpp?view=msvc-160

https://blog.csdn.net/ych9527/article/details/111134855?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162915797716780271532935%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=162915797716780271532935&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v29-2-111134855.pc_search_result_cache&utm_term=%E9%93%BE%E6%8E%A5%E5%B1%9E%E6%80%A7%E5%92%8C%E4%BD%9C%E7%94%A8%E5%9F%9F&spm=1018.2226.3001.4187

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值