c++随笔1

C语言和C++都有一个专为调试而准备的工具函数,就是 assert()函数

这个函数是在C语言的 assert.h 库文件里定义的,

所以包含到C++程序里我们用以下语句:#include <cassert>

作用是,如果它的条件返回错我,则终止程序的执行。

 

区分char *和char []:char *定义的是一个指向字符串的指针(注意:C语言中没有对应字符串的内置类型或者类类型),而char []就是C语言中的用来定义字符数组(注意:字符数组是不同于字符串,如果字符数组以'\0'结尾,那么可以视为字符串)。

char a[]在运行时赋值,值会从静态区赋值到函数的栈中,对它进行修改不会产生任何问题。char *a在编译时就确定了,a指向静态区中的值,没有赋值到函数栈中, 因此对指针的内容进行修改会产生错误

了解一下 一个由 C / C++ 编译的程序占用的内存分为以下几个部分: 
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由 OS 回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后由系统释放 。
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。

 

关于头文件和源文件的分别

首先,我们可以将所有东西都放在一个.cpp文件内.

然后编译器就将这个.cpp编译成.obj,obj是什么东西?

就是编译单元了.一个程序,可以由一个编译单元组成,

也可以有多个编译单元组成. 如果你不想让你的源代码变得很难阅读的话,

就请使用多个编译单元吧.(一个函数不能放到两个编译单元里面,但两个以上

就可以分别放在一个单元,也就是cpp里面)

    那么就是一个.cpp对应一个.obj,然后将所有的obj链接起来(通过一个叫链接器的程序),

组成一个.exe,也就是程序了.

    如果一个.cpp要用到另一个.cpp定义的函数怎么办? 只需在这个.cpp种写上他的函数声明

就可以了.其余工作由链接器帮你完成,你可以随便调用该函数.

    链接器将所有的obj连接起来,但是如果碰巧有相同的函数或外部变量怎么办?他如何识别?

一般来说是不能允许在同一个程序中,出现两个一样的函数名或外部变量名.

    但是只得庆幸的是,c++可以通过一种叫做链接属性的关键字来限定,你这个函数是属于整个程序

公用的,还是只是在一个编译单元obj里面使用的.

    这些关键字就是extern 和 static extern是外部链接的意思,也就是除了这个单元,外部的单元也是能够访问这个函数的.static 是内部链接,自属于自己单元.

说了这么久,还没有说.h的作用呢?

    其实没有.h也能很好的工作,但是当你发现一个外部链接的函数或外部变量,需要许多份

声明,因为c++这种语言,在使用函数和变量的时候,必须将他声明,为何要声明?声明之后才

知道他的规格,才能更好的发现不和规格的部分.你别妄想一个编译单元,会自动从另一个

编译单元那里得到什么信息,知道你是如何定义这个函数的.

    所以说,只要使用到该函数的单元,就必须写一份声明在那个.cpp里面,这样是不是很麻烦,

而且,如果要修改,就必须一个一个修改.这真让人受不了.


.h就是为了解决这个问题而诞生,他包含了这些公共的东西.然后所有需要使用该函数的.cpp,只需要

用#include包含进去便可.以后需要修改,也只是修改一份内容.


请注意不要滥用.h,.h里面不要写代码,.h不是.cpp的仓库,什么都塞到里面.

如果在里面写代码,当其他.cpp包含他的时候,就会出现重复定义的情况,

比如将函数func(){printf};放到头文件a.h,里面还有一些a.cpp需要的声明等;

然后你发现b.cpp需要用到a.cpp里面的一个函数,就很高兴的将a.h包含进来.

注意,#include并不是什么申请指令,他就是将指定的文件的内容,原封不动的拷贝

进来.


这时候实际上a.cpp和b.cpp都有一个func()函数的定义.

如果这个函数是内部链接static的话,还好,浪费了一倍空间;

如果是extern,外部链接(这个是默认情况),那么根据在同一个程序内不可出现

同名函数的要求,连接器会毫不留情给你一个连接错误!

 

为了避免同一个文件被include多次

1   #ifndef方式
2   #pragma once方式

在能够支持这两种方式的编译器上,二者并没有太大的区别,但是两者仍然还是有一些细微的区别。
    方式一:

    #ifndef __SOMEFILE_H__
    #define __SOMEFILE_H__
    ... ... // 一些声明语句
    #endif

    方式二:

    #pragma once
    ... ... // 一些声明语句


    #ifndef的方式依赖于宏名字不能冲突,这不光可以保证同一个文件不会被包含多次,也能保证内容完全相同的两个文件不会被不小心同时包含。当然,缺点就是如果不同头文件的宏名不小心“撞车”,可能就会导致头文件明明存在,编译器却硬说找不到声明的状况

    #pragma once则由编译器提供保证:同一个文件不会被包含多次。注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。带来的好处是,你不必再费劲想个宏名了,当然也就不会出现宏名碰撞引发的奇怪问题。对应的缺点就是如果某个头文件有多份拷贝,本方法不能保证他们不被重复包含。当然,相比宏名碰撞引发的“找不到声明”的问题,重复包含更容易被发现并修正。

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 int main()
 4 {
 5     printf("The program test print style!\n");
 6     /* 以十进制形式输出带符号整数(正数不输出符号) */
 7     printf("%d\n", 223);
 8     printf("%d\n", -232);
 9     printf("\n");
10     /* 以八进制形式输出无符号整数(不输出前缀O) */
11     printf("%o\n", 223);
12     printf("%o\n", -232);
13     printf("\n");
14     /* 以十六进制形式输出无符号整数(不输出前缀OX) */
15     printf("%x\n", 223);
16     printf("%x\n", -232);
17     printf("\n");
18     /* 以十进制形式输出无符号整数 */
19     printf("%u\n", 223);
20     printf("%u\n", -232);
21     printf("\n");
22     /* 以小数形式输出单、双精度实数 */
23     printf("%f\n", 223.11);
24     printf("%f\n", 232.11111111);
25     printf("%f\n", -223.11);
26     printf("%f\n", -232.11111111);
27     printf("\n");
28     /* 以指数形式输出单、双精度实数 */
29     printf("%e\n", 223.11);
30     printf("%e\n", 232.11111111);
31     printf("%e\n", -223.11);
32     printf("%e\n", -232.11111111);
33     printf("\n");
34     /* 以%f%e中较短的输出宽度输出单、双精度实数 */
35     printf("%g\n", 223.11);
36     printf("%g\n", 232.111111111111);
37     printf("%g\n", -223.11);
38     printf("%g\n", -232.111111111111);
39     printf("\n");
40     /* 输出单个字符 */
41     printf("%c\n", 'a');
42     printf("%c\n", 97);   //输出为  'a'
43     printf("\n");
44     /* 输出单个字符 */
45     printf("%s\n", "this is a test!");
46     printf("%s\n", "2342o34uo23u");
47     printf("\n");
48     getch();
49 }

assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行

 

C编译器要求变量的声明需要放在作用域的开头,这里是函数的开始处。而C++编译器不要求在哪用就在哪声明,也可以在开头声明好。所以,写C++代码是就比较放得开,什么时候需要什么时候声明就可以了。

 

sizeof(数组名)计算数组大小并不是每次都管用哦。sizeof(数组名)/sizeof(数组一个元素)也并不是每次都可以正确计算数组元素个数哦。

强行记忆学不好编程

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 int main()
 4 {
 5     char url[] = "www.baidu.com";
 6     char bb[] = { 0,1,2,3,4 };
 7     int age[]= { 0,1,2,3,4 };
 8     int isizeurl = sizeof(url);
 9     int isizebb = sizeof(bb);
10     int isizeage = sizeof(age);
11     printf("isizeurl:%d\n", isizeurl);
12     printf("isizebb:%d\n", isizebb);
13     printf("isizeage:%d", isizeage);
14     getchar();
15 }

你可以看到,sizeof(数组名)是可以计算得到数组的字节大小的。这并不是全部的事实!

 

Linux系统下C程序开发详解:

1 .ii为扩展名的文件,是已经预处理过的C++源代码文件,也是中间代码文件。 

2  .o为扩展名的文件,是编译后的目标文件,源文件生成的中间目标文件。

3 .s为扩展名的文件,是汇编语言源代码文件。 

4 .S为扩展名的文件,是经过预编译的汇编语言源代码文件。 

5 .o为扩展名的文件,是编译以后的程序目标文件(Object file),目标文件经过连接成可执行文件

 

 

注意 NULL 就是 NULL,它被宏定义为 0

  #define NULL 0

 很多系统下除了有 NULL 外,还有 NUL

NUL 是 ASCII 码表的第一个字符,表示的是空字符,其 ASCII 码值为 0。其值虽然都为 0,但表示的意思 完全不一样。

 

函数宏中的#和##运算符

在函数宏中#可以实现由函数宏实参生成字符串常量##实现了由函数宏实参生成标识符的一部分。(前者用于拼接字符串后者用于拼接标示符)看一下下边的示例:

假如希望在字符串中包含宏参数,ANSI C允许这样作,在类函数宏的替换部分,#符号用作一个预处理运算符,它可以把语言符号转化程字符串。例如,如果x是一个宏参量,那么#x可以把参数名转化成相应的字符串。该过程称为字符串化

1 #include<stdlib.h>
2 #include<stdio.h>
3 #define psqs(x) printf("the square of"#x "is %d.\n",(x)*(x))
4 int main(void) {
5     int y = 4;
6     psqs(y);
7     psqs(2 + 4);
8     system("pause");
9 }

第一次调用宏时使用“y”代替#x;第二次调用时用“2+4″代#x。

##运算符可以使用类函数宏的替换部分。另外,##还可以用于类对象宏的替换部分。这个运算符把两个语言符号组合成单个语言符号。例如:

#define XNAME(n) x##n

这样宏调用:

XNAME(4)

展开后:x4

 

posted @ 2016-05-20 20:02  M_Lion  阅读(78)  评论(0编辑  收藏  举报