《C专家编程》学习笔记1

1.整形升级和寻常算术转换:

char,short int 或者位段,包括他们的有符号和无符号型,以及枚举类型,可以使用在需要的int 或unsignede int 的表达式中。如果int可以完整的表示源类型的所有值,那么该源类型的值就转换为int  型,否则转换为无符号类型,这称之为整形升级。

对于其他类型的转换,如果有两个操作数:转换关系如下:

long double   ------   others  type      --------------->    long  double

double          -------   others type       -------------->     double

float              ------    others type          -------------->    float

否则进行整形升级,

unsigned  long int    --------- others type  ----------->    unsigned long int

long  int    -----------------------   unsigned int    如果长整形能够完整的表示无符号整形的所有值,那么无符号整形被转换为长整型,否则全部转换为无符号长整形。

建议:在使用位段和二进制掩码的时候才使用无符号数,其他情况下建议不要使用这类无符号的类型。

NULL表示什么也不指向(空指针),NUL表示用于结束一个ASCII字符串。

switch语句的一些问题;

default 可以自由出现在任意的位置,如果没有它且没有与之匹配的case那么,这个switch语句不做任何事,C语言不检查这种情况,PASCAL语言却是检查的。

每个case和default的顺序可以是任意的,习惯上把default放在最后。

在每个case后没有break的情况称之为“fall through"这种情况下需要特殊注释出来。

理解C语言的优先级规则:

A.声明从它的名字开始读取,然后按照优先级顺序依次读取。

B.优先级从高到底依次是:

1.声明中被括号括起来的那部分。

2.后缀操作符:

括号()表示这是一个数组,而方括号【】表示这是一个数组。

3.前缀操作符:*星号表示“指向。。。。。。的指针”。

C。如果const 或 volatile 关键字的后边紧跟类型说明符,那么它作用于类型说明符。在其他的情况下,作用于它左边紧邻的指针星号。

注意typedef 和define 的区别

typedef  不可以组合定义,define只作用于第一个参数。

声明与定义的区别:

定义:只出现在一个地方,确定对象的类型并分配内存,用于创建新的对象,例如int a[100];  相当于特殊的声明并且为对象分配内存。

声明:可以多次出现,描述对象的类型,用于指代其他地方定义的对象(extern  int   a  ; )说明的并非自身,而是描述其他地方的对象。

左值与右值的区别:

x  =  y ;

x是左值本质表示一个地址,右值Y在这里本质表示一个内容。

数组引用与指针引用的区别:

char  a[9] = "abcdefgh"  ;

c = a[i]      ;

首先编译器会取a的地址就是,数组元素的首地址,然后取i的值与首地址相加

然后取出地址内的内容。

char * p  ;

c  =   *p  ;

首先编译器符号表有一个符号P,假设它的地址为5000,

然后编译器会取出地址5000的内容就是数组的首地址,然后取出地址内的内容。

当定义为指针,但以数组方式引用时,这时候的取值方法是上边两个方式的混合。

现在总结如下:

1.取得符号表中p的地址,提取存储于此处的指针。

2.把下标所表示的偏移量与指针的值相加,产生一个地址。

3.访问上面这个地址取得字符。

定义指针时,编译器并不为指针所指向的对象分配空间,它只是分配指针本身的空间,除非在定义的同时赋给指针一个字符串常量进行初始化。其中字符串常量被存放在只允许读的文本段中,防止被修改。

再探数组与指针:

我们先从声明开始:

声明本身还可以进一步分成3种情况:

1.外部数组的声明。

2.数组的定义。

3.函数参数的声明。

所有作为函数参数的数组名总是可以通过编译器转换为指针。

(小心在一个文件中定义为数组,在另一个文件中声明为指针这种情况)

所以:数组的声明就是数组,指针的声明就是指针,对于编译器而言,一个数组就是一个地址,一个指针就是一个地址的地址。

在表达式中,指针和数组是可以互换的,因为他们的最终形式都是指针。

但是为什么C语言把数组形参当作指针,这只是出于效率的考虑。

没有办法把数组本身传递给一个函数,因为它总是被自动转换为指向数组的指针,由此sizeof运算符不再可以帮助我们求数组的长度,这种求法只可以在本地栈中使用。

现在:

数组和指针可交换性总结:

1.想a[1]总是被编译器改写为*(a+1)这种情况。

2.指针始终是指针它决不可以改写成数组,但是可以用下标访问的方式,访问内容,也可以作为参数传递。

3.在特定的上下文中,也就是它作为函数的参数(有且只有这一种情况)一个数组的声明可以看做是一个指针。作为函数参数的数组始终会被编译器修改成为指向数组第一个元素的指针。

4.因此,把一个数组定义为一个函数参数的时候你可以任意选择是定义成指针还是定义为数组,毕竟编译器得到的只是一个指针。

5.在其他的情况中,声明和定义必须匹配。

向函数传递一个数组的方法:

1.增加一个额外的参数,表示元素的数目。

2.在数组的最后添加一个特殊值,提示它是数组的结尾,因为C语言不检查结尾。

关于指针和数组的最后一个问题:

如和交换两个值:

<span style="font-size:24px;">temp = a    ;
a    = b    ;
b    = temp ;

a ^= b ;
b ^= a ;
a ^= b ;

a = a+b;
b = a-b;
a = a-b;</span>

其实这几个交换方式各有优缺点:

第一种方法:明显要使用一个中间变量,

第二种方法:必须满足条件1.a与b都是原子操作且不会发生硬件失败,空间不够或数学运算失败。

第三种方法:有越界的可能性。

内存中的 “猫腻” :

C语言:先编译后连接。

B语言:先连接后编译。

段:

UNIX中,段表示一个二进制文件相关的内容块。

在inter X86中,段表示一种设计结果。是一些大小为64K的区域。

一般C程序有三个基本的段:BSS段,文本段,数据段。具体参见之前的一篇博文‘C与内存’。

堆栈段:

包含一种单一的数据结构,就是栈。

其中包含一个全局的指针SP用来提示栈的顶端。

作用:

1.堆栈为函数内部声明的局部变量提供存储空间。

2.进行函数调用时,堆栈存储与此有关的一些维护性信息,另外一个更常用的名字是过程活动记录。

3.堆栈也可以被用作暂时存储区。

堆栈是向下增长的,

过程活动记录:

简而言之,这个东西是C语言自带的用来监控被调用函数的。

包含:

局部变量,参数,静态链接,指向先前结构体的指针,返回地址。

在内存中这也是向下增长的。也就是朝着低地址方向生长。

时间: 2025-01-13 09:18:15

《C专家编程》学习笔记1的相关文章

C专家编程学习 1

1.C语言的基本数据类型直接与底层硬件相对应. 2#define 是可能出现问题 1 2 3 4 5 #define a(y) a_ex(y) a(x)被扩展为 a_ex(x) #define a (y) a_ex(y) a(x)被扩展为 (y) a_ex(y)(x) #define宏的用法 1.简单宏定义 1 #define a y 将文件中的 a 全部换成 y 为了避免出现问题,要将宏展开,根据运算符的优先级判断是否是需要的运算顺序. 2.带参数的宏定义 1 2 #define a(y) a

Java并发编程学习笔记

Java编程思想,并发编程学习笔记. 一.基本的线程机制 1.定义任务:Runnable接口 线程可以驱动任务,因此需要一种描述任务的方式,这可以由Runnable接口来提供.要想定义任务,只需实现Runnable接口并编写run方法,使得该任务可以执行你的命令.   class MyTask implements Runnable {    private String mName;     public MyTask(String name) {    mName = name;   }  

Linux Shell脚本编程学习笔记和实战

http://www.1987.name/141.html shell基础 终端打印.算术运算.常用变量 Linux下搜索指定目录下特定字符串并高亮显示匹配关键词 从键盘或文件中获取标准输入 [read命令] 文件的描述符和重定向 数组.关联数组和别名使用 函数的定义.执行.传参和递归函数 条件测试操作与流程控制语句 获取时间日期格式和延时 [date.sleep命令] 内部字段分隔符IFS和脚本的调试DEBUG 显示.读取或拼接文件内容 [cat命令] 文件查找与打印文件列表 [find命令]

linux网络编程学习笔记之二 -----错误异常处理和各种碎碎(更新中)

errno 在unix系统中对大部分系统调用非正常返回时,通常返回值为-1,并设置全局变量errno(errno.h),如socket(), bind(), accept(), listen().erron存放一个正整数来保存上次出错的错误值. 对线程而言,每个线程都有专用的errno变量,不必考虑同步问题. strerror converts to English (Note: use strerror_r for thread safety) perror is simplified str

JAVA GUI编程学习笔记目录

1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之GUI编程窗体事件 6.JAVA之GUI编程Action事件 7.JAVA之GUI编程鼠标事件 8.JAVA之GUI编程键盘码查询器 9.JAVA之GUI编程列出指定目录内容 10.JAVA之GUI编程弹出对话框Dialog 11.JAVA之GUI编程菜单 12.JAVA之GUI编程打开与保存文件 13.JAVA之GUI编程将程序打包jar JA

黑马程序员_JAVA UDP网络编程学习笔记

一.UDP网络编程概述 采用TCP协议通信时,客户端的Socket必须先与服务器建立连接,连接建立成功后,服务器端也会持有客户端连接的Socket,客户端的Socket与服务器端的Socket是对应的,它们构成了两个端点之间的虚拟通信链路.与TCP通信不同,UDP是面向无连接的.不可靠的基于数据包的传输协议.即应用进程(或程序)在使用UDP协议之前,不必先建立连接.自然,发送数据结束时也没有连接需要释放.因此,减少了开销和发送数据之前的延时.UDP也采用端口来区分进程. 在java中,java.

Java并发编程学习笔记(一)线程安全性 1

什么是线程安全性: 要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的和可变的状态的访问."共享"意味着变量可以由多个线程同时访问,而"可变"则意味着变量的值在其生命周期内可以发生变化. 一个对象是否需要线程安全的,取决于他是否被多个线程访问.这指的是在程序中访问对象的方式,而不是对象要实现的功能.要使得对象时线程安全的,需要采用同步机制来协同对对象可变状态的访问.如果无法实现协同,那么可能导致数据破坏以及其他不该出现的结果. 如果当多个线程访

FFmpeg编程学习笔记一

FFmpeg编程学习笔记一 1.为了学习ffmpeg编程需要单步调试,参照网上的教程用VS2013编译一次成功,之后随便写了个重采样音轨小程序,也就是把一个5.1声道的AC3文件分解成6个WAV文件的简单功能. 2.编译成功执行也正常,但速度奇慢,比同类软件eac3to慢了约5倍. 3.OK上网搜搜咋回事,一天,二天过去了无果. 4.无奈用VS2013的性能与诊断,分析出最占用时间的函数调用具然是:swr_convert参数里的lrintf() lrint() llrint() llrintf(

Android Socket编程学习笔记

通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄.在Internet上的主机一般运行了多个服务软件,同时提供几种服务.每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务. 网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket.Socket通常用来实现客户方和服务方的连接.Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定. 在java中,Socke

linux网络编程学习笔记之六 -----I/O多路复用服务端

多进程和多线程的目的是在于最大限度地利用CPU资源,当某个进程不需要占用太多CPU资源,而是需要I/O资源时,可以采用I/O多路复用,基本思路是让内核把进程挂起,直到有I/O事件发生时,再把控制返回给程序.这种事件驱动模型的高效之处在于,省去了进程和线程上下文切换的开销.整个程序运行在单一的进程上下文中,所有的逻辑流共享整个进程的地址空间.缺点是,编码复杂,而且随着每个逻辑流并发粒度的减小,编码复杂度会继续上升. I/O多路复用典型应用场合(摘自UNP6.1) select的模型就是这样一个实现