C语言--字符串、指针、预处理指令(#include|#define|#if)

字符串


c中没有类似java的字符串类型

用一个一维的char数组来存字符串, 尾部必须有一个空字符(‘\0’)来作为结束:

1
2
3
4
5
6
7
char a[] = "stone"; //自动在尾部追加空字符
char b[2]= {'a', '\0'}; //空字符也占有一个元素位置
char c[2]={'a', 'b'}; //这并不是一个字符串 以printf("%s", c); 来输出 结果与预期不符
char names[2][10] = { {'J','a','y','\0'}, {'J','i','m','\0'} }; //这样的二维数组,其中每个一维数组,都浪费了6个空间

指针


指针用来指向一块内存地址。如:int a = 10; int p = &a; int c = p;

字符串指针:

char p = “admin”, char q = p; char q = p; 与 char q = &p[0]; 效果一样的(数组的首元素地址就是该数组的地址) 。 而 char *q=&p[1]; 打印q, 输出:”dmin”

  • 遍历:

    1
    2
    3
    for (; *p != '\0'; p++) {
    printf("=%c ", *p);
    }

    当使用指针遍历后,这时地址位已经移到结束符的位置了。所以想重置时,需要 p -= 5;

  • 交换字符:

1
2
3
4
5
void swap(char *p,char *q) {
char temp = *p;
*p = *q;
*q = temp;
}
  • 交换字符串:
1
2
3
4
5
void swapstr(char **p, char **q) {
char *temp = *p;
*p = *q;
*q = temp;
}

指针和数组的互换

形参数组,实参指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void change(int b[]) {
b[0] = 10;
}
int main() {
// 定义一个int类型的数组
int a[4] = {1, 2, 3, 4};
int *p = a;
// 将数组名a传入change函数中
//change(a);
change(p); //直接传入指针变量
// 查看a[0]
printf("a[0]=%d", a[0]);
return 0;
}

形参指针,实参数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void change(int *b) {
b[0] = 10;
// 或者*b = 10;
b[1] = 11;
// 或 *(b+1) = 11;
}
int main(int argc, char *argv[]) {
// 定义一个int类型的数组
int a[4] = {1, 2, 3, 4};
// 将数组名a传入change函数中
change(a);
//遍历
int len = sizeof(a) / sizeof(int);
for (int i=0; i<len; i++) {
printf("%d\n", a[i]);
}
return 0;
}

错误使用举例

首先要牢记一点,指针表示一个内存地址。

  1. char *a = "stone";*a = "aaa"; //error

a 表示 a指向的值,而字符串就是一组字符序列,所以实际 a指代的是字符数组a 的首位的值

简单的说: 不能将一个常量值,赋给另一个常量

正确的写法:a = "aaa"; //将常量赋给变量

  1. 1
    2
    char b[] = "stone";
    b = "aaaa"; //error

    这里 b 还是指代的字符数组的首地址。 参照上面的遍历 来理解。

    正确代码,需要用一个临时的指针变量:

    1
    2
    char *p = b;
    p = "aaaa"; //fine
  2. 计算数组的长度。

    1
    2
    int a[4] = {1, 2, 3, 4};
    int len = sizeof(a) / sizeof(int);

    如上,能得出一个正确的值。

    但,若是将该数组传递到一个函数里,再进行计算可能就有问题了

    1
    2
    3
    void calc_len(int *p) {
    int len = sizeof(p) / sizeof(int); //p表示地址 len=2
    }

    解决方法:在外部计算好数组长度,再传递给函数。

返回指针的函数

返回指针的函数的一般形式为:类型名 *函数名(参数列表)

1
char * getstr() {...}

指向函数的指针

定义的一般形式:函数的返回值类型(*指针变量名)(形参1, 形参2,...) = 函数名;

1
2
3
4
5
6
7
8
9
int sum(int a, int b) {
return a + b;
}
int main() {
int (*q) (int a, int b) = sum;// (int a, int b) 可以写成(int a, int)或(int,int)或()
int result = (*q)(2,5);//调用函数
printf("\n%d", result);
return 0;
}

将函数指针作为形参

1
2
3
4
5
6
7
8
9
void get(int (*p)(), int a, int b) {//第一个参数即为函数形参
//void get(int (*p)(int, int), int a, int b) { //与上一句 等价
int result = (*p)(a, b);
printf("sum = %d\n", result);
}
int main() {
int (*q)() = sum;
get(*q, 10, 20);
}

预处理指令


C语言在对源程序进行编译之前,会先对一些特殊的预处理指令作解释(比如#include文件包含指令),产生一个新的源程序(这个过程称为编译预处理),之后再进行通常的编译

预处理指令可以出现在程序的任何位置,它的作用范围是从它出现的位置到文件尾。

#define 用来定义一个宏

不带参数的宏定义

格式:define 宏名 字符串, 如define ABC 10

右边的字符串也可以省略,比如#define ABC

它的作用是在编译预处理时,将源程序中所有”宏名”替换成右边的”字符串”,常用来定义常量

宏名的有效范围是从定义位置到文件结束。如果需要终止宏定义的作用域,可以用#undef ABC命令

省略右边字符串的宏, 主要用于 『条件编译』指令中

带参数的宏定义:宏函数

格式:define 宏名(参数列表) 字符串 注意:宏名和参数列表间不能有空格

在编译预处理时,将源程序中所有宏名替换成字符串,并且将 字符串中的参数用 宏名右边参数列表中的参数替换

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#define average(a, b) (a+b)/2
int main ()
{
int a = average(10, 4); // 替换成 (10+4)/2
printf("平均值:%d", a);
return 0;
}

引用已经定义的宏名

1
2
3
#define R 3.0
#define PI 3.14
#define L 2*PI*R

条件编译

程序的其中一部分代码只有在满足一定条件时才进行编译,

否则不参与编译(只有参与编译的代码最终才能被执行),这就是条件编译

语法格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
#if 条件1
...code1...
#elif 条件2
...code2...
#else
...code3...
#endif //条件编译结束标记 必须有

一般用来判断宏或常量, 不能判断 非预编译 的 运行时 变量。 例:

1
2
3
4
5
6
7
8
9
#define MAX 5
#if MAX == 0
printf("MAX是0");
#elif MAX > 0
printf("MAX大于0");
#else
printf("MAX小于0");
#endif

判断是否定义过宏:

1
2
3
4
#if defined(MAX)// <==> #ifdef MAX
...code...
#elif !defined(MAX)// <==> #ifndef MAX
#endif

文件包含指令

第1种形式#include<文件名>

直接到C语言库函数头文件所在的目录中寻找文件

1
2
#include <stdio.h>
#include <string.h>

第2种形式#include”文件名”

这种形式下,系统会先在源程序当前目录下寻找,若找不到,再到操作系统的path路径中查找,最后才到C语言库函数头文件所在目录中查找

注意:#include指令允许嵌套包含,比如a.h包含b.h,b.h包含c.h,

但是不允许递归包含,比如 a.h 包含 b.h,b.h包含 a.h

使用#include指令可能导致多次包含同一个头文件,降低编译效率

解决方法:在包含的文件里,加上条件编译指令,防止函数被重复定义, 如:

1
2
3
4
5
6
#ifndef c_program_cc_h
#define c_program_cc_h
method 1();
method 2();
………….
#endif

C中没有类似java的package的概念,它只有文件系统。

如果要引入当前目录下的文件夹util中的calcUtil.h : #include "calcUtil.h"

------ 本文结束 ------

版权声明
协议:No Fuck License

stone 创作并维护
本文首发于 stone世界 博客( http://stone86.top
版权所有,侵权必究。如要转载,请声明出处

Fork me on GitHub