最近在看《C和指针》,复习基础也很重要,开篇文章记录下要点方便以后复习。
1.条件操作符
例如:
a>5?b-6:c/2
可以读作“a是不是大于5?如果是就执行b-6,否则执行c/2
在什么场合使用比较好呢
例如:
if(a>5)
1
2
|
<code> b[
2
*c+d(e/
5
)]=
3
;
</code>
|
else
1
2
|
<code><code> b[
2
*c+d(e/
5
)]=
20
;
</code></code>
|
这里改用条件操作符就很方便
b[[2*c+d(e/5)]=a>5?3:20
2.计算字符串长度
虽然库里面已经有了,但是自己可以编写一个学习一下
int strlen(char *string)
{
1
2
|
<code><code><code>
int
length=
0
;
</code></code></code>
|
/* 依次访问字符串内容,计算字符数,直到遇见NUL终止符*/
1
2
3
4
5
6
|
<code><code><code><code>
while
(*string++!=
'\0'
length+=
1
;
return
length;
</code></code></code></code>
|
}
3.递归问题
例如:需要将一个整数转换成可以打印的字符形式,一般采用的策略就是反复除以10,并打印余数,但是转换成字符有个简便的方式
‘0’+0=’0’;
‘0’+1=’1’;
‘0’+2=’2’;
这样余数加上‘0’就可以转化了,但是输出的值是反着的,故可用递归的思想解决
void binary2ascii(unsigned int value)
{
1
2
3
4
5
6
7
8
9
10
|
<code><code><code><code><code>unsigned
int
quo;
quo=value/
10
;
if
(quo!=
0
)
binary2ascii(quo);
putchar(value%
10
+
'0'
);
</code></code></code></code></code>
|
}
4.字符数组的初始化
按照一般的理解,你可能会认为字符数组的初始化是这样
char message [] ={‘H’,’e’,’l’,’l’,’o’,0};
这个方法看起来很笨拙,有更高端的写法
char message[]=”Hello”;
尽管看起来像字符串,实际上不是以下才是初始化字符串
char *message=”Hello”;
5.作为函数参数的多维数组
int matrix[3][10]
…
func2(marix)
/有以下两种方法声明函数/
void func2(int (*mat)[10] );
void func2(int mat[ ][ ]);
另外值得注意的是,多维数组的存储顺序是根据最右边的下标优先变化的原则确定。
6.关于字符串查找
6.1查找一个字符
在一个字符串中查找一个字符最简单的方法是使用
strchr和
strrchr函数,函数原型如下
1
2
3
4
5
|
<code><code><code><code><code><code><code>
char
*strchr(
char
const
*str,
int
ch);
char
*strrchr(
char
const
*str,
int
ch);
</code></code></code></code></code></code></code>
|
注意第二个参数是个整型值,
strchr在字符串str中ch第一次出现的位置,找到后函数返回一个指向更改位置的指针。如果该字符并不存在与字符串中,函数就返回一个NULL指针。
str函数功能与其类似,只是它返回的是该字符串中该字符最后出现的位置。例如:
1
2
3
4
5
6
7
8
9
|
<code><code><code><code><code><code><code><code><code>
char
string[
20
]=
"Hello there"
;
char
*check;
check=strchr(string,
'h'
);
</code></code></code></code></code></code></code></code></code>
|
check所指向的位置是string+7,因为第一个
h出现在这个位置,注意此处大小写是有区别的。
6.2查找人和几个字符串
strpbrk是个更为常见的函数,它并不是查找某个特定的字符串,而是查找任何一组字符第一次在字符串中出现的位置,它的原型如下:
1
2
3
4
5
|
<code><code><code><code><code><code><code><code><code><code>
char
*strpbrk(
char
const
*str ,cahr
const
*group);
</code></code></code></code></code></code></code></code></code></code>
|
这个函数返回一个指向str 中第一个匹配group中任何一个字符的字符位置,如果未找到匹配,函数返回一个NULL指针。例如:
1
2
3
4
5
6
7
8
9
|
<code><code><code><code><code><code><code><code><code><code>
char
string[
20
]=
"Hello there"
;
char
*check;
check=strpbrk(string ,
"aeiou"
);
</code></code></code></code></code></code></code></code></code></code>
|
check所指向的位置是string+1,因为这个位置是第二个参数中字符第一次出现的的位置,和上一个一样,也是区分大小写。
7.字符分类函数
以下函数如果参数满足条件就返回真(包含在ctype.h)
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<code><code><code><code><code><code><code><code><code><code>
1
.isspace --------空白字符:空格,换页,换行,回车等等
2
.isdigit --------十进制数
0
-
9
3
.islower --------小写字母a~z
4
.isupper --------大写字母A~Z
5
.isalpha --------字母a~z或者字母A~Z
6
.ispunct --------标点符号
</code></code></code></code></code></code></code></code></code></code>
|
例如:
1
2
3
4
5
6
7
8
9
|
<code><code><code><code><code><code><code><code><code><code>
if
(ch>
'A'
&& ch<
'Z'
) 这条语句只能在ASCII机器上运行
可用以下语句替换则无论那个机器都能使用
if
(isupper(ch))
</code></code></code></code></code></code></code></code></code></code>
|
8.关于结构体
声明结构体时可使用的一种良好技巧是用
typedef创建一种新的类型,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<code><code><code><code><code><code><code><code><code><code><code>
typedef struct{
int
a;
char
b;
float
c;
}Simple;
...
Simple x;
Simple y[
20
],*z;
</code></code></code></code></code></code></code></code></code></code></code>
|
9.函数指针
简单声明一个函数指针并不意味马上可以使用,访问前必须初始化指向某个函数,例如:
1
2
3
4
5
6
7
8
9
|
<code><code><code><code><code><code><code><code><code><code><code>
int
fun(
int
)
int
(*pf)(
int
) =&fun;
//之后我们就可以这样使用
int
ans;
ans =f(
25
);
ans =(*pf)(
25
);
ans =pf(
25
);</code></code></code></code></code></code></code></code></code></code></code>
|
9.1回调函数
例如,你需要编写函数比较整数大小,但同时需要用于其他字符串的比较,解决方案就是使用函数指针,你只需要编写一个函数,用于比较两个值,然后把一个指向这个函数的指针作为参数传递给查找函数,然后查找函数来执行值的比较,使用这种方法任何类型都可以进行比较。
注意:我们必须想函数传递指针而不是这个值本身,函数有个*void 形参,用于接收这个参数,然后指向这个值的指针便传递给比较函数。
使用这种技巧的函数被称为“回调函数”,因为用户把一个函数指针作为参数传递给其他函数,后者将“回调”用户的函数。(由于不知道进行比较的值,一般把参数类型声明为*void,表示“一个指向未知类型的指针”。例如下面的比较函数,它用于在链表中进行查找:
1
2
3
4
5
6
7
8
9
10
11
|
<code><code><code><code><code><code><code><code><code><code><code>
int
compare(
void
const
*a ,
void
const
*b)
{
if
(*(
int
*)a ==*(
int
*)b )
return
0
;
else
return
1
;
}
//这个函数将这样使用
Node *search_list(Node*node,
void
const
*value,
int
(*compare)(
void
const
*,
void
ocnst *));
....
abc= search_list(root,&value,compare);</code></code></code></code></code></code></code></code></code></code></code>
|
注意:强制类型转换:比较函数的形参必须声明为void*以匹配查找函数的原型,然后在强制类型转换为int*类型,用于比较整数。
如果你希望在字符串列表中查找,则可这样写:
1
2
|
<code><code><code><code><code><code><code><code><code><code><code>#include <string.h>
abc=search_list(root,&value,strcmp);</string.h></code></code></code></code></code></code></code></code></code></code></code>
|
10.命令行参数
大家都知道C程序main函数具有两个形参,第一个通常是argc,它表示的命令行参数的数目,第二个是argv,它指向这组参数值(数组)的第一个元素。如果程序需要访问命令行参数,需要在main函数声明时加上这些参数。
1
2
|
<code><code><code><code><code><code><code><code><code><code><code>
int
main(
int
argc,
char
**argv)
/*这两个参数的命名无所谓</code></code></code></code></code></code></code></code></code></code></code>
|
假设命令行如下:
1
|
<code><code><code><code><code><code><code><code><code><code><code>$ cc -c -o main.c insert.c -o test</code></code></code></code></code></code></code></code></code></code></code>
|
11.字符串常量
看下面的语句
1
|
<code><code><code><code><code><code><code><code><code><code><code>
"xyz"
+
1
</code></code></code></code></code></code></code></code></code></code></code>
|
他似乎是个垃圾,这货居然试图在字符串上面进行某种加法运算。但实际上它是指针值加一,结果仍然是个指针,指向字符串中第二个字符:y
所以
1
2
|
<code><code><code><code><code><code><code><code><code><code><code>*
"xyz"
--
1
"xyz"
[
2
] --
2
</code></code></code></code></code></code></code></code></code></code></code>
|
这个式子1结果就是x 。注意结果不是整个字符串,只是第一个字符。
式子2结果就是字符z 。
上面曾经有将二进制转换成字符并且打印出来,现在改成16进制形式打印出来怎么做呢?
可以这样:
1
2
3
4
5
|
<code><code><code><code><code><code><code><code><code><code><code>remainder =value %
16
;
if
(remainder<
10
)
putchar(remainder+
'0'
);
else
putchar(remainder-
10
+
'A'
);</code></code></code></code></code></code></code></code></code></code></code>
|
另将二进制数转换成字符还有种骚操作
1
|
<code><code><code><code><code><code><code><code><code><code><code>putchar(
"0123456789ABCDEF"
[value%
16
]);</code></code></code></code></code></code></code></code></code></code></code>
|
12.条件编译
下面显示了基本的条件编译形式
1
2
3
|
<code><code><code><code><code><code><code><code><code><code><code>#
if
constant
statement
#endif</code></code></code></code></code></code></code></code></code></code></code>
|
此时若constant非零(真)那么statement就被正常编译,否则不执行。
1
2
3
4
5
6
7
|
<code><code><code><code><code><code><code><code><code><code><code>#
if
constant
statement
#elif constant1
other statement
#
else
constant2
others
#endif</code></code></code></code></code></code></code></code></code></code></code>
|
13.输入输出函数
13.1错误报告
perror是一种简单的方式进行报告错误
它的原型定义于stdio.h
1
|
<code><code><code><code><code><code><code><code><code><code><code>
void
perror(
char
const
*message);</code></code></code></code></code></code></code></code></code></code></code>
|
如果message 不是Null并且指向一个非空的字符串,perror就会打印出这个字符串,后面跟一个分号和空格,然后打印出一条用于解释该错误的信息。
13.2打开流
应该注意的是应该时刻检查fopen函数的返回值!如果函数失败,他会返回一个NULL值,如果程序不检查错误,这个NULL函数就会传给后续的I/O函数。
1
2
3
4
5
6
7
|
<code><code><code><code><code><code><code><code><code><code><code>FILE *input
input =fopen (
"data3"
,
"r"
);
if
(input ==NULL)
{
perror(
"data"
);
exit(EXIT_FALLURE);
}</code></code></code></code></code></code></code></code></code></code></code>
|
13.3printf家族
这个家族共有三个函数:fprintf,printf,sprintf;
1
2
3
|
<code><code><code><code><code><code><code><code><code><code><code>
int
fprintf(FILE*stream,
char
const
*format,...);
int
printf(
char
const
*format,....);
int
sprintf(
char
*buf ,
char
const
*format);</code></code></code></code></code></code></code></code></code></code></code>
|
使用printf,结果输出送到标准输出;
使用fprintf,你可以使用任何输出流;
而sprintf把他的结果作为一个NUL结尾的字符串存储到指定的buf缓冲区而不是写入流中。(这种方式在嵌入式开发中经常用到)。
注意:sprintf缓冲区的大小并未给定,可能会溢出,故应声明一个足够大的缓冲区。
14.标准库函数
标准库函数是个很好的工具箱,有很多实用工具我们未曾注意,下面将进行简单汇总
14.1 算术 ‘stdlib.h’
1
|
<code><code><code><code><code><code><code><code><code><code><code>
int
abs(
int
value)</code></code></code></code></code></code></code></code></code></code></code>
|
abs函数返回他的参数的绝对值
1
|
<code><code><code><code><code><code><code><code><code><code><code>p_t p(
int
num,
int
den);</code></code></code></code></code></code></code></code></code></code></code>
|
p函数把他的第2个参数除以第1个参数,产生商和余数,用一个p_t结构返回,这个结构包含两个字段
1
2
|
<code><code><code><code><code><code><code><code><code><code><code>
int
quot;
//商
int
rem ;
//余数</code></code></code></code></code></code></code></code></code></code></code>
|
14.2随机数’stdlib.h’
1
2
|
<code><code><code><code><code><code><code><code><code><code><code>
int
rand(
void
);
void
srand(unsigned
int
seed);</code></code></code></code></code></code></code></code></code></code></code>
|
一般使用时,使用每天的时间作为随机数产生的种子,例如
1
2
3
|
<code><code><code><code><code><code><code><code><code><code><code>srand((unsigned
int
)time(NULL));
...
sth =rand()%i;</code></code></code></code></code></code></code></code></code></code></code>
|
14.3字符串转换’stdlib.h’
1
2
|
<code><code><code><code><code><code><code><code><code><code><code>
int
atoi(
char
const
*string); ---
1
long
int
strtol(
char
const
*string,
char
**unused ,
int
base); ---
2
</code></code></code></code></code></code></code></code></code></code></code>
|
第一个函数把字符转换成整数
第二个函数如果第二个参数非NULL,则保存在第二个参数所指向的位置
14.4浮点表示’math.h’
1
|
<code><code><code><code><code><code><code><code><code><code><code>
double
modf(
double
value ,
double
*ipart);</code></code></code></code></code></code></code></code></code></code></code>
|
该函数把一个浮点值分成整数和小数部分,每个部分都具有和原值一样的符号,整数部分以double类型存储与第二个参数所指向的内存位置,小数部分作为函数的返回值。
14.5幂级数’math.h’
1
2
|
<code><code><code><code><code><code><code><code><code><code><code>
double
pow (
double
x,
double
y);
double
sqrt(
double
x);</code></code></code></code></code></code></code></code></code></code></code>
|
pow函数返回X的Y次方的值,由于计算中可能遇到对数,所以如果x是一个负数且y不是一个整数,就会报错。
sqrt函数返回参数的平方根,如果参数为负,同样会报错。
14.6字符串转换’stdlib.h’
1
2
|
<code><code><code><code><code><code><code><code><code><code><code>
double
atof(
char
const
*string);
double
strtod(
char
const
*string,
char
**unused);</code></code></code></code></code></code></code></code></code></code></code>
|
用法和上面14.3类似,返回的是double
14.6排序和查找
最后记录下标准库里面自带的排序和查找函数
qsort函数在一个数组中以升序的方式对数据进行排序,由于他是和类型无关的,所以你可以使用qsort排序任意类型的数据,只是数组中的元素长度固定。
1
2
|
<code><code><code><code><code><code><code><code><code><code><code>
void
qsort(
void
*base,size_t n_elements,size_t el_size,
int
(*compare)(
void
const
*,
void
const
* ));</code></code></code></code></code></code></code></code></code></code></code>
|
第一个参数指向需要排序的数组,第二个参数指定数组中元素的数目,第三个参数指定每个元素的长度(以字符为单位)。第四个参数是一个函数指针,一般为比较函数。在排序时候,qsort调用这个函数对数组中的数据进行比较,通过传递一个指向合适的比较函数的指针,你可以实用qsort函数排序任意类型的数组。
比较函数接受两个*void参数,在比较重必须强制类型转换,具体说明见9.1,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<code><code><code><code><code><code><code><code><code><code><code>
//使用qsort对一个元素为某种结构的数组进行排序
#include <stdlib.h>
#include <string.h>
typedef struct {
char
key[
10
];
/*数组排序关键字
int other_data; /*与关键字关联的数据
}Record
int compare(void const *a, void const *b){
return strcmp((Record*)a)->key,((Record*)b)->key);
}
int main()
{
Record array[ 50 ];
qsort(array ,50 ,sizeof(Record) ,compare);
/*此时数组已排序完毕*/
return
EXIT_SUCCESS;
}</string.h></stdlib.h></code></code></code></code></code></code></code></code></code></code></code>
|
bsearch函数在一个已经排序好的数组中用二分法查找一个特定元素。
1
2
|
<code><code><code><code><code><code><code><code><code><code><code>
void
*bsearch(
void
const
*key,
void
const
*base ,size_tn_elements,
size_t el_size,
int
(*compare)(
void
const
*,
void
const
*));</code></code></code></code></code></code></code></code></code></code></code>
|
第一个参数只想你要查找的值,第二个参数指向查找的数组,第三个参数指定数组元素的数目,第四个参数制定每个元素的长度,最后一个参数适合sqort中一样指向比较函数的指针)。
bserach会返回一个指向查找到的数组元素的指针,若未找到会返回NULL。
用法上面类似,依然以上面的例子:
创客学院在线视频学习:www.makeru.com.cn C语言学习群:197416462