C/C++学习笔记:基础知识7

1 字符和字符串

在 C 语言中字符用它们的字符集值对应的小整数表示。因此, 你不需要任何转换函数: 如有你有字符, 你就有它的值。

数字字符和它们对应的 0-9 的数字之间相互转换时, 加上或减去常数 ’0’, 也就是说, ’0’ 的字符值。

字符串用字符数组表示; 通常你操作的是字符数组的第一个字符的指针,C语言从来不会把数组作为一个整体操作 (赋值, 比较等)。

strcat(string, "!");    //正确
strcat(string, '!');   //错误,  strcat() 用于连接字符串

 if(string == "value")    //错误
//上面代码段中的 == 操作符比较的是两个指针 —— 指针变量 string 的值和字符串常数 “value” 的指针值 —— 看它们是否相等, 
//也就是说, 看它们是否指向同一个位置。它们可能并不相等, 所以比较决不会成功。

// 较两个字符串, 一般使用库函数 strcmp():
if(strcmp(string, "value") == 0) 
{
     /* string matches "value" */
}

char a[] = "Hello, world!"; 
char b[14]; 
b = "Hello, world!";     //错误,字符串是数组, 而你不能直接用数组赋值。
strcpy(b, "Hello, world!");  //正确,使用 strcpy() 代替

cout << sizeof("a") << endl;    //2,"a" 是字符串
cout << sizeof('a') << endl;    //1,  'a' 是字符

char r = 'abc';
cout << sizeof(r) << endl;  //1, ???
cout << sizeof('abc') << endl;  //4,  ???

2   布尔表达式和变量

C 语言没有提供标准的布尔类型,可以使用以下任何一种形式。

#define TRUE 1 
#define FALSE 0 

#define YES 1
#define NO 0

enum bool {false, true}; //默认地,第一个枚举成员赋值为0,后面的每个枚举成员赋的值比前面的大1

enum bool {no, yes};

//或直接使用 1 和 0 , 只要在同一程序或项目中一致即可。
//如果你的调试器在查看变量的时候能够显示枚举常量的名字, 可能使用枚举更好。
//有些人更喜欢这样的定义
#define TRUE (1==1)
#define FALSE (!TRUE)
//或者定义这样的 “辅助” 宏
#define Istrue(e) ((e) != 0)


C 语言中的确任何非零值都都被看作真, 但这仅限于 “输入”, 也就是说, 仅限于需要布尔值的地方。内建操作符生成布尔值时, 可以保证为 1 或 0。

一般规则是只在向布尔变量赋值或函数参数中才使用 TRUE 和 FALSE (或类似的东西), 或者用于函数的返回值, 但决不用于比较

预处理宏 TRUE 和 FALSE (当然还有 NULL) 只是用于增加代码可读性, 而不是因为其值可能改变

尽管使用 TRUE 和 FALSE 这样的宏 (或者 YES 和 NO) 看上去更清楚, 布尔值和定义在 C 语言中的复杂性让很多程序员觉得 TRUE 和 FALSE 宏不过更令人迷惑, 因而更喜欢使用 1 和 0


3 头文件

(1)程序分成多个源文件, 把什么放到 .c 文件, 把什么放到 .h 文件
作为一般规则, 把如下东西放入头 (.h) 文件中:
· 宏定义 (预处理 #defines)
· 结构、联合和枚举声明
· typedef 声明
· 外部函数声明
· 全局变量声明
当声明或定义需要在多个文件中共享时, 尤其需要把它们放入头文件中。特别是, 永远不要把外部函数原型放到 .c 文件中。
另一方面, 如果定义或声明为一个 .c 文件私有, 则最好留在 .c 文件中。


(2)头文件定义技巧是:

#ifndef HFILENAME_USED
#define HFILENAME_USED
     //... 头文件内容 ...
#endif
每一个头文件都使用了一个独一无二的宏名。这令头文件可自我识别,以便可以安全的多次包含; 而自动 Makefile 维护工具可以很容易的处理嵌套包含文件的依赖问题

(3 )  #include <>  语法通常用于标准或系统提供的头文件, 而 #include ""  通常用于程序自己的头文件。

用 <> 括起来的头文件会先在一个或多个标准位置搜索。用 "" 括起来的头文件会首先在 “当前目录” 中搜索, 然后 (如果没有找到) 再在标准位置搜索。

(4) 根据 “缺少的” 头文件的种类, 有几种情况。
如果缺少的头文件是标准头文件, 那么你的编译器有问题。你得向你的供货商或者精通你的编译器的人求助。

对于非标准的头文件问题更复杂一些。有些完全是系统或编译器相关的。某些是完全没有必要的, 而且应该用它们的标准等价物代替。例如, 用 <stdlib.h> 代替 <malloc.h>。其它的头文件, 如跟流行的附加库相关的, 可能有相当的可移植性。

标准头文件存在的部分原因就是提供适合你的编译器, 操作系统和处理器的定义。你不能从别人那里随便拷贝一份就指望它能工作, 除非别人跟你使用的是同样的环境.


 (5  )参数个数可变的宏
用一个单独的用括弧括起来的的 “参数” 定义和调用宏, 参数在宏扩展的时候成为类似 printf() 那样的函数的整个参数列表。

#define DEBUG(args) (printf("DEBUG: "), printf args)
if(n != 0) DEBUG(("n is %d\n", n));
//明显的缺陷是调用者必须记住使用一对额外的括弧。

 

#if 预处理指令只处理整数

#pragam 指令提供了一种单一的明确定义的 “救生舱”, 
可以用作各种 (不可移植的) 实现相关的控制和扩展: 源码表控制、结构压缩、警告去除 (就像 lint 的老 /* NOTREACHED */ 注释), 等等

#pragma once  某些预处理器实现的扩展用于使头文件自我识别


4  输入输出

scanf("%d", i); //错误,传给 scanf() 的参数必须是指针。
 scanf("%d", &i)  // 正确
 char s[30]; scanf("%s", s); 
 // 不用 & 也可以。需要指针; 并不表示一定需要 & 操作符。
 // 当你向 scanf() 传入一个指针的时候, 你不需要使用 &, 因为不论是否带 & 操作符, 数组总是以指针形式传入函数

 在 scanf() 转换数字的时候, 它遇到的任何非数字字符都会终止转换并被保留, 除非采用了其它的步骤, 那么未预料到的非数字输入会不断“阻塞” scanf():

scanf() 永远都不能越过错误的非数字字符而处理后边的合法数字字符。如果用户在数字格式的 scanf 如 %d 或 %f 中输入字符 ‘x’, 那么提示后并用同样的 scanf() 调用重试的代码会立即遇到同一个 ’x’。


用 scanf %d 读取一个数字, 然后再用 gets() 读取字符串, 但是编译器好像跳过了 gets() 调用?

scanf %d 不处理结尾的换行符。如果输入的数字后边紧接着一个换行符, 则换行符会被 gets() 处理。

作为一般规则, 你不能混用 scanf() 和 gets(), 或任何其它的输入例程的调用; scanf 对换行符的特殊处理几乎一定会带来问题。要么就用 scanf() 处理所有的输入, 要么干脆不用

 在 printf 的格式串中输出一个 ’%’

 只需要重复百分号: %%,而 \%不行, 因为反斜杠 \是编译器的转义字符, 而这里我们的问题最终是 printf 的转义字符



读取二进制数据文件的时候你应该用 “rb” 调用 fopen(), 确保不会发生文本文件的解释。类似的, 写二进制文件时, 使用 “wb”。

注意文本/二进制区别只是发生在文件打开时: 一旦文件打开之后, 在其上调用何种 I/O 函数无关紧要。


同时向两个地方输出, 如同时输出到屏幕和文件?

直接做不到这点。但是你可以写出你自己的 printf 变体, 把所有的内容都输出两次。下边有个简单的例子:

#include <stdio.h>
#include <stdarg.h>
void f2printf(FILE *fp1, FILE *fp2, char *fmt, ...)
{
va_list argp;
va_start(argp, fmt); vfprintf(fp1, fmt, argp); va_end(argp);
va_start(argp, fmt); vfprintf(fp2, fmt, argp); va_end(argp);
}
//这里的 f2printf() 就跟 fprintf() 一样, 除了它接受两个文件指针并同时输出到两个文件

5 产生标准分布或高斯分布的随机数?

#include <stdlib.h>
#include <math.h>
double gaussrand()
{
	static double V1, V2, S;
	static int phase = 0;
	double X;

	if(phase == 0) 
	{
		do {
			double U1 = (double)rand() / RAND_MAX;
			double U2 = (double)rand() / RAND_MAX;
			V1 = 2 * U1 - 1;
			V2 = 2 * U2 - 1;
			S = V1 * V1 + V2 * V2;
		} 
		while(S >= 1 || S == 0);
		X = V1 * sqrt(-2 * log(S) / S);
	} 
	else
	{
		X = V2 * sqrt(-2 * log(S) / S);
	}
	phase = 1 - phase;
	return X;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值