c和指针

作者: 云水波痕 | 来源:发表于2017-04-10 22:28 被阅读0次

一、数组

1. 初始化

  • 静态变量仅被初始化一次,当没有显式给出初始值时,编译器会自动将其初始化为0。静态变量在程序运行之前就已经被初始化。
  • 对于自动变量,由于被存放在堆栈中,所以执行六每次执行到其代码所在时都会为其分配不同的内存位置。所以编译器没有办法对其进行初始化,因此在没有给出显式初始值时,编译器不会对齐进行默认初始化。

由于数组进行初始化时,会进行多次幅值操作,所以在程序执行流多次执行到数组初始化语句时会对数组进行重新初始化,因此也会带来很多额外的开销。因此将数组声明成static,会带来更好的执行效率,只初始化一次数组。

  • 下标引用的优先级高于间接访问。

二、字符数组

1. 字符串处理

头文件: <string.h>

字符串处理函数:
  • strcpy:字符串拷贝,以NULL结束
  • strcmp:字符串比较
  • strlen :返回字符串长度(不包含NULL),返回类型为size_t(unsigned int),定义在<stddef.h>
  • strcat:字符串拼接函数
字符串查找函数
  • strchr/strrchr:查找字符串中某个字符第一次或最后一次出现的位置(地址)
  • strstr:查找字符串中另一个字符串第一次出现的位置
  • strpbrk:在一个字符串中查找指定字符串中任意字符第一次出现的位置。
  • strspn/strcpsn:计算一个字符串的起始部分(不)匹配一个指定字符集中任意字符的字符数量。
  • strtok:把一个字符串分割成几个标记。每次当它调用时,都返回一个指向字符串中下一个标记位置的指针。这些标记由一个指定的字符集的一个或多个字符分割。
#include <stdio.h>
#include <string.h>

int main(int agrc, char* argv[])
{
    char token[] = "\t\n";
    char source[] = "cui\ttengju";
    char *t = NULL;

    for (t=strtok(source,token); t!=NULL; t= strtok(NULL,token))
    {
        printf("%s\n", t);
    }

    return 0;
}

2.字符处理函数

头文件:
<ctype.h>

字符检测函数
  • iscntrl
  • isdigit
  • isxdigit
  • isalpha
  • isprint
  • isspace
  • islower
  • isupper
  • isalnum:是否为字母或数字
  • ispunt:是否为标点符号
字符转换函数
  • tolower
  • toupper

三、 结构和联合体

1. 结构

结构可以将不同类型的变量存储在一起。这些不同类型的变量成称为结构体成员。

结构体声明

结构体的声明列出了结构包含的成员列表。不同的结构体声明,即使他们的成员完全相同也是不同的结构体类型。如果希望在不同的位置声明相同结构类型的结构体变量可以使用结构标签,结构标签与成员列表相关联。采用结构标签进行结构体声明,就不用重复声明。

struct tag{
    int a;
    double d;
};  //声明结构体

struct tag A;  //定义结构体变量

typedef同样可以实现上述目标

typedef struct{
    int a;
    double d;
}SIMPLE;

SIMPLE A; //定义结构体变量

结构体中不能包含自身的引用,即不能包含本身。但是可以包含指向自身结构体的指针

结构体的边界对齐

为了提高存取结构体类型的效率,编译器会对结构体成员进行字节对齐处理。
位于<stddef.h>中的offsetof(sturct tag , d)可以获取结构体成员相对结构体起始位置的偏移位置。返回值为size_t(unsigned int)
为了节省内存空间,我们可以在编程时,将对于边界对齐敏感的成员放到结构体开始的位置。可以一定程度节省内存开销。

结构体作为函数参数

由于函数调用,其参数采用传值方式。所以为了提高函数的效应效率,建议当结构体作为函数参数时,尽量采取传址方式
传址方式的一个缺点就是函数中可以通过结构体地址修改原结构体中的成员,为了避免这种情况的发生,可以将形参声明为const

2. 联合体

联合体的声明与结构类似,但它其中的谁有成员共用一块内存。
对于联合体的初始化,必须是联合体的第一个成员类型。如果给出的初始值不是第一个成员的类型,也会被转化成一个成员的数据类型。

四、函数指针

函数指针主要有两种用途:1.转换表(函数指针数组);2.最为函数的参数

double add(double,double);
double sub(double,double);
double mul(double,double);
double div(double,double);

double (*oper_func[])(double,double); //函数指针数字

double ret = oper_func[0](a,b);  //引用,转换表的实现
  1. 对函数指针进行间接访问之前需要对齐进行初始化。
  2. 对函数进行初始化之前,其初始值的函数原型必须已经声明,否者编译器无法检测函数指针与初始值的类型一致性。
  3. 对函数指针的初始化可以采取赋值操作。例如:
int f(int);
int (*pf)(int) = f;  //函数名总是被编译器转换成函数函数指针

//或者
int (*pf)(int) = &f;
  1. 对函数的使用我们可以采取2种方式,例如:
1. int ret = pf(25);
2. int ret = (*pf)(25);

五、字符串常量

当一个字符串常量出现在表达式中时,它的值是一个指针常量。编译器会把这些指定的字符的一份拷贝存储在内存的某个位置,并存储一个指向第一个字符的指针。

printf("%c\n",*("xyz"+1));  //结果'y'
printf("%c\n",*("xyz"));  //结果为'x'

printf("%c\n","xyz[2]");  //结果为'z'

六、文件流操作

/*打开特定的文件,并把一个流与该文件相关联*/
FILE *fopen(char const *name, char const *mode); //成功返回指向FILE结构的指针;失败返回NULL

始终检查fopen函数的返回值。任何可能错误的操作都要进行检测


类型 读取 写入 追加
文本 r w a
二进制 rb wb ab
  1. 如果一个文件用于读取,则它之前必须已经存在。
  2. 如果一个文件用于写入,当它不存在时将被创建;当它已经存在时,之前存在的内容将被删除。
  3. 如果一个文件用于追加写入,当它不存在时将被创建;当它已经存在时将会在其原内容之后追加新的内容。

如果mode的值为"a+",那么打开该文件,是为了更新该文件的内容,且允许读写操作。如果在向其写入数据之前,已经从该文件中读取了一部分数据,此时需要程序员使用函数fflush或文件定位函数重新定位文件指针的位置,然后进行写入操作。读取操作亦如此。

/*关闭文件流*/
int fclose(FILE *f); //对于输出流,关闭文件流之前会刷新缓冲区。函数执行成功返回0,否者返回EOF

字符I/O

1. 字符输入
int fgetc(FILE *stream);
int getc(FILE *stream); //fgetc,getc缺省状态下,作用于stdin
int getchar(void);  //只作用于stdin。每个函数从流中读取下一个字符,并将其作为函数的返回值。如果流中不存在更多的字符,返回EOF。之所以返回值定义为int,正式因为EOF的实际值比一个字符要多几位,EOF被定义为整型。
2. 字符输出
int fputc(int character, FILE *stream); 
int putc(int character, FILE *stream);
int putchar(int character); //只作用于stdout。打印之前会将整型参数裁剪为unsigned char。当写入到未打开的stream,返回EOF。

fgetcfputc都是函数,而getc/putc/getchar/putchar都是通过#define指令定义的宏。宏在执行效率上稍高,但是函数在程序长度方面更胜一筹。

3. 字符的撤销
int ungetc(int character, FILE* stream); //把一个先前读入的字符返回到流中,这样它可以在以后被重新腐乳。
4. 字符串的处理
char *fets(char *buffer,int buffer_size, FILE *stream);//最多读取buffer_size-1个字符,但流中的字符并不会丢失,下一次fgets会从下一个字符开始读取。任何情况下,buffer都是以'\0'结尾。如果在未读取任何字符之前,流已经到达文件尾,那么fets返回NULL,否者返回buffer。
char *gets(char *buffer); //只作用于stdin。读入字符串时,并不会在缓存区中存储结尾的换行符。


char *fputs(char const *buffer, FILE *stream);//写入过程中出现错误返回EOF,否者返回非负数。
char *puts(char const *buffer); //只作用于stdout。会在输出字符串之后再添加一个换行符。

由于gets函数并没有指定缓存区的长度,所以难免会出现缓存区溢出的问题,所以不建议使用该函数。


5. 格式化字符串处理

格式化输入

int fscanf(FILE *stream, char const *format, ...); //从输入流中读取字符并根据format字符串将其转换。当格式化字符串到达末尾或读取的字符不在匹配格式化字符串指定的类型时,输入结束。函数返回被转换的输入值的数目,如果没有输入值被转换,则返回EOF。

//后两者与fscanf唯一的不同就是输入源的不同。
int scanf(char const *format,...); //输入源为stdin
int sscanf(char const *string, char const *format, ...); //输入源为给定的字符串。

格式化输出

int fprintf(FILE *stream,char const *format,...);
int printf(char const *format,...);
int sprintf(char *buffer,char const *format);//sprintf可以将它的结果作为一个NULL结尾的字符串存储在指定的buffer缓存区中,而不是写入到流中。
6. 二进制I/O
size_t fread(void *buffer, size_t size,size_t count,FILE *stream); //用于读取二进制数据

size_t fwrite(void *buffer, size_t size,size_t count,FILE *stream); //用于写入二进制数据

//buffer是一个指向保存数据的内存位置的指针;size是缓冲区中每个元素的字节数;count是读取或写入的元素数;stream是数据读取或写入的流。函数的返回值时实际读取或写入的元素的个数。
7. 刷新或定位文件指针
int fflush(FILE *stream); //迫使一个输出流的缓存区内的数据进行物理写入,无论缓存区是否已经写满。

当我们需要将缓存区的数据立即进行物理写入时,可以使用fflush

long ftell(FILE *stream); //返回流的当前位置,也就是下一次操作流的开始位置相对于文件起始位置的偏移量。

int fseek(FILE *stream, long offset, int from); //设置下一次流操作的位置

|form|说明|
|-|-|-|
|SEEK_SET|从流的起始位置offset个字节,offset必须是一个非负值|
|SEET_CUR|从流的当前位置起offset个字节,offset的值可正可负|
|SEET_END|从流的尾部位置起offset个字节,offset的值可正可负。如果是正值,定位到文件尾的后面|

int feof(FILE *stream); //如果流当前处于文件尾,该函数返回真。

七、标准库函数

1. 整型函数

随机数<stdlib.h>:

  • int rand(void); 产生一个介于0 ~ RAND_MAX之间的伪随机数。
  • void srand(unsigned int seed); 为了避免每次运行获取相同的随机数,我们可以调用srand函数初始化随机说发生器。一个常用的技巧即使使用时间作为随机数差生的seed
    例如:
srand((unsigned int)time(0)); //以时间为种子初始化随机数发生器
rand(); //产生随机数

字符串转换<stdlib.h>
忽略前导空白字符

int atoi(char const *string);
long atol(char const *string);//基数为10

long strtol(char const *string, char **unused, int base);
unsigned long strtoul(char const *string, char **unused, int base);// unused保存不能被转换的第一个字符的指针的地址

//如果这些函数中不含有合法的数值,函数将返回0;

//字符串到浮点数的转换
double atof(char const *string);
double strtod(char const *string, char **unused);

例:

#include <stdio.h>
#include <stdlib.h>

int main(int argc,char **argv)
{
    char name[10] ="  32cui";
    char *unused = NULL ;
    long res = strtol(name,&unused,10);
    puts(unused);
    return EXIT_SUCCESS;
}
执行结果

浮点型函数<math.h>

  • double sqrt(double x);
  • double exp(double x );
  • double log(double x); 以e为底
  • double log10(double x);
  • double pow(double x,double y); 幂函数
  • double ceil(double x); 返回不小于参数的最小整数
  • double floor(double x); 返回不大于其参数的最大整数
  • double fmod(double x,double y); 返回余数,该函数的商被限定为整数

日期和时间函数<time.h>

clock_t clock(void); //函数返回从程序开始执行到目前为止处理器所消耗的时间(近似值);返回值为处理器时钟的滴答次数,可以通过乘以CLOCKS_PER_SEC将其转换为秒。

time_t time(time_t *returned_value); //返回当前日期和时间(从1970/1/1至今流逝的秒数),失败返回-1。

//time_t处理
char *ctime(time_t const *time_vlalue); //处理time_t变量,将其时间进行格式化返回。
double difftime(time_t time1, time_t time2); //计算两者的差值,并将其转换为秒。

//time_t --> tm结构
struct tm *gmtime(time_t const *time_value);  //转化为世界协调时间/UTC/格林尼日标准时间
struct tm *localtime(time_t const *time_value); //转换为当地时间

//tm结构 ——> time_t
time_t mktime(struct tm *tm_ptr);
tm结构字段

相关文章

  • C语言05- 指针

    C语言05- 指针 13:指针 指针是C语言中的精华,也是C语言程序的重点和难点。 13.1:指针定义与使用 指针...

  • C++ 指针常量、常量指针和常指针常量

    参考:C++ 指针常量、常量指针和常指针常量

  • c和指针

    一、数组 1. 初始化 静态变量仅被初始化一次,当没有显式给出初始值时,编译器会自动将其初始化为0。静态变量在程序...

  • 学习笔记3(指针运算,函数参数与指针,数组指针,二级指针)

    一、指针运算 二、数组与指针 三、指针和函数参数 java中: C/C++中: 四、指针数组 五、 二级指针 六、...

  • 带小白学C语言指针

    C语言里指针才是C语言的开始和指针;C语言里基本所有东西都是由指针演变而成; 指针是指向地址的变量,类型就是指针...

  • C语言

    C 指针、指针变量、函数指针、指针函数、指针数组、数组指针、C 数组

  • C语言函数指针和指针函数

    C语言函数指针和指针函数 在学习C语言的过程中,“指针函数”和“函数指针”经常容易搞混了, 最简单的辨别方式就是看...

  • Go教程第十篇:指针

    指针 在这篇教程中,我们将学习Go里面的指针是如何工作的,以及Go的指针和C/C++的区别和差异。 什么是指针 ?...

  • 读书笔记

    笔记地址:《C和指针》《C专家编程》

  • C++ 、java 和 C# 的区别

    一、基础类型 c++: ** java:** C#: 二、指针 1.java 是没有指针这个概念,c++ 和 c#...

网友评论

    本文标题:c和指针

    本文链接:https://www.haomeiwen.com/subject/vrbeattx.html