边界检查
尽管列表没有固定的大小,但是Python仍然不允许引用不存在的元素,超出列表末尾之外的索引总是会导致错误,对列表末尾范围之外的赋值也是如此。
为了让一个列表增大,我们可以调用append这样的列表方法。
常量是一旦初始化后就不能改变的变量,在C++中使用const指定常量。
计算机内存中的一块区域,变量可以存储任何事,而且可以改变。
1 | a = 1 |
1 | _a = 1 |
这里有一个global的关键字,如果没有这个关键字是不对的,结果可能对,但是实际的运行不是你认为的那样
1 | _a = 1 |
define
宏是在预处理阶段展开。const
常量是编译运行阶段使用。define
宏没有类型,不做任何类型检查,仅仅是展开。const
常量有具体的类型,在编译阶段会执行类型检查。define
宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。const
常量会在内存中分配(可以是堆中也可以是栈中)。const
定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而 #define定义的常量在内存中有若干个拷贝。
编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
作为常量,两者都有很多的应用,在一个程序里面看不到const
和define
的可能性不大。
C++ 语言可以用const
来定义常量,也可以用#define
来定义常量。但是前者比后者有更多的优点:比较重要的是const
常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应)。
得到随机数的代码:
1 | srand(time(NULL)); //use clock value as starting seed。这个保证每次出来的随机数都不同 |
在做for循环的时候,循环控制变量也可以是一个浮点类型的变量。例如:
for(double x = 1.0; x < 11; x += 1.0),但是由于浮点类型在计算机内部的表示形式,决定了分数值通常没有浮点数形式的精确表示,所以不应该把相等判断作为结束循环的条件,比如x != 2.0,就可能一直打不到要求。
与大多数现代编程语言一样,C语言也没有输入输出的能力,所有这类操作都由标准库中的函数提供。
stderr
流只是将来自C库的错误信息传送出去,也可以将自己的错误信息传送给stderr
。
stderr
和stdout
之间的主要差别是,输出到stdout
的流在内存上缓存,所以写入stdout
的数据不会马上送到设备上,而stderr
不缓存,所以写入到stderr
的数据会立刻传送到设备上。
对于缓存的流,程序会在内存中传入或传出缓存区域的数据,在物理设备上传入或传出数据可以异步进行。这使输入输出比较高效,而为错误信息使用不缓存的流,其优点是可以确保错误信息显示出来,但输出操作是低效的。缓存的流比较高效,但是如果程序因某种原因失败,缓存的流就不会刷新,所以输出可能永远不会显示出来。
scanf
会忽略空白字符scanf
和printf
中有个格式控制符%n
:表示输出有效字符的数量。
N多的scanf
参数也可以保证,你可以用许多方式得到自己希望得到的数据。
但是有一点需要注意,就是scanf
对输入格式很挑剔,稍微错一点就会导致整个读取输入出错。
scanf
的格式控制符%s
只能读取不含空格的字符串,但是%[]
可以读取包含空格的字符串,比如I love you,就可以全部读取。
scanf
的陷阱对于字符串输入,使用gets或fgets通常是首选方式,除非要控制字符串的内容。
这一节接触的都是新东西,hold住。
数据运算符顾名思义就是对数据进行操作的运算符,这里设计到几个新概念,如果刚开始不了解,也没有关系,毕竟是进阶的东西,先大概看一下有个基础概念,因为这一次我们接触到了C语言中最难懂也是最优雅的指针,此处有掌声。
数据运算符有下面几个:
数据运算符 | 含义 |
---|---|
sizeof() | 获取xx的大小,返回字节长度 |
[] | 数组 |
& | 取地址 |
* | 定义指针或者解指针 |
-> | 结构体解应用 |
. | 结构体应用 |
详细说说这几个的具体含义:
sizeof(xx)
:这个比较简单,就是计算xx的字节长度;[]
:数组的定义方式,比如int a[10]就是定义了一个一维的整数数组,这个数组长度为10,但是有个问题要注意了,数组的长度从0开始,所以如果访问数组a
,只能是a[0]到a[9]
,如果下标是10就越界报错了,其实ANSI也曾尝试着把这个下标从1开始矫正过来,但是几十年的传统还有兼容性,此事只能作罢;&
:取地址是取一个变量的地址,比如int a=0
;我们顶一个了一个整数a
,它的值是0
,那么它位于哪里呢,这个就类似于你叫张三,你家的地址在桃花坞里桃花庵,桃花庵里第一间,另注意这个符号在C++
里面是引用的含义,不要混淆;*
:这个星号就大有来头了,前后左右的含义不一样,古往今来也很很多人栽进去了,就是因为这个隐晦又高效的指针,所以有了一本书叫做《C和指针》,专门来讲讲这个东西,后面我们也会 有系列小篇,争取能让大家越过这个坎,那就算是C语言里面的高手了;->和.
:这两个的区别主要在于结构体,如果定义的结构体是指针用->
,如果不是用.
,这个我也迷惑过。看个例子来show一下:
这个例子又新增了一些东西,除了上面描述的运算符,我们增加了一个头文件string.h
,这个用来后面调用strcpy
函数,另外还使用了结构体,下一节来聊聊必须要使用的结构体。
1 | /*advance/operator/operator3.c*/ |
相应地Makefile
如下所示:
1 | #advance/operator/Makefile |
输入make
,然后./operator3
输出为:
1 | sizeof(a) is 4 |
Q&A :上上节的答案,为什么a没有赋值却有这么大的值,这个涉及到后面会说到的全局变量和局部变量的区别,局部变量在未初始化之前,有可能是任何值,所以为防意外,变量定义最好都要初始化。
我利用此方法成功在UBUNTU 10.04下安装GTK 2.20.1。
1、安装gcc/g++/gdb/make 等基本编程工具
1 | $sudo apt-get install build-essential |
2、安装 libgtk2.0-dev libglib2.0-dev 等开发相关的库文件
1 | $sudo apt-get install gnome-core-devel |
3、用于在编译GTK程序时自动找出头文件及库文件位置
1 | $sudo apt-get install pkg-config |
4、安装 devhelp GTK文档查看程序
1 | $sudo apt-get install devhelp |
5、安装 gtk/glib 的API参考手册及其它帮助文档
1 | $sudo apt-get install libglib2.0-doc libgtk2.0-doc |
6、安装基于GTK的界面GTK是开发Gnome窗口的c/c++语言图形库
1 | $sudo apt-get install glade libglade2-dev |
或者
1 | $sudo apt-get install glade-gnome glade-common glade-doc |
7、安装gtk2.0 或者 将gtk+2.0所需的所有文件统通下载安装完毕
1 | $sudo apt-get install libgtk2.0-dev |
或者
1 | $sudo apt-get install libgtk2.0* |
1、查看1.2.x版本
1 | $pkg-config –modversion gtk+ |
2、查看 2.x 版本
1 | $pkg-config –modversion gtk+-2.0 |
3、查看pkg-config的版本
1 | $pkg-config –version |
4、查看是否安装了gtk
1 | $pkg-config –list-all grep gtk |
1 | //Helloworld.c |
1、编译
1 | $gcc -o Helloworld Helloworld.c `pkg-config –cflags –libs gtk+-2.0` |
2、运行
1 | $./Helloworld |
赋值运算符就是把表达式或者值赋给变量,比如极易混淆的=
为赋值,等于是==
。
其实赋值运算符可以和很多前面说的算术运算符、逻辑运算符和按位运算符合并使用。
如下如下:
赋值运算符 | 含义 |
---|---|
= | 赋值,等于 |
+= | 加后赋值 |
-= | 减后赋值 |
*= | 乘后赋值 |
/= | 除后赋值 |
%= | 取模后赋值 |
&= | 按位与后赋值 |
|= | 按位或后赋值 |
^= | 按位异或后赋值 |
<<= | 按位左移后赋值 |
>>= | 按位右移后赋值 |
举个例子来show一下:
1 | /*advance/operator/operator2.c*/ |
相应地Makefile
如下所示:
1 | #advance/operator/Makefile |
输入make
,然后./operator2
输出为:
1 | a is 107106770 |
可以看到结果就是对每一位进行的操作
那么问题来了,你知道为什么a刚开始的值是107106770吗,为什么每次都给a赋值吗?