C语言的基础知识2
1、字符串、字符和字节
1.1 字符串的长度,size_t strlen() 不包括\0,注意:size_t 为无符号整型
1.2 不受限制的字符串函数
1.2.1 复制字符串 char *strcpy(char *dst,char const *src);这个函数不检查src和dst的长度,会造成溢出。返回第1个参数的一份拷贝,是指针拷贝,是dst字符串的指针。
1.2.2 连接字符串 char *strcat(char *dst,char const *src);把src字符串添加到dst字符串的后面。也不检查长度,会溢出。返回第1个参数的一份拷贝,是dst字符串的指针。
1.2.3 字符串比较 int strcmp(char const *s1,char const *s2);
1.3 长度受限的字符串函数,可以有效防止溢出
char *strncpy(char *dst,char const *src,size_t len);如果strlen(src)长度大于等于len,末尾不会以NUL字符结尾
char buffer[BSIZE];
strncpy(buffer,name,BSIZE);
buffer[BSIZE-1]='\0';
char *strncat(char *dst,char const *src,size_t len);最多项目表数组复制len个字符,再加一个末尾的NUL字节。
int strncmp(char const *s1,char const *s2,size_t len);
1.4 字符串查找基础
1.4.1 查找一个字符
char *strchr(char const *str,int ch);查找字符串str中字符ch第一次出现的位置,然后返回一个指向该位置的指针,如果不存在,返回NULL。
char *strrchr(char const *str,int ch);查找字符串str中字符ch最后一次出现的位置,然后返回一个指向该位置的指针,如果不存在,返回NULL。
1.4.2 查找任何几个字符
strbrk是个更为常见的函数,查找任何一组字符第一次在字符串中出现的位置,只要group中出现的任意一个字符都行
char *strbrk(char const *str,char const *group);
如果找到就返回一个指向该位置的指针,如果没有,则返回NULL
1.4.3 查找一个子串
char *strstr(char const *s1,char const *s2);
这个函数在s1中查找整个s2第一次出现的起始位置,并返回一个指向该位置的指针。如果没有找到,返回NULL。如果s2是一个空字符串,函数就返回s1。
1.5 高级字符串查找
1.5.1 查找一个字符串前缀
size_t strspn(char const *str,char const *group);返回str起始部分匹配group中任意字符的字符数(直到不匹配)
size_t strcspn(char const *str,char const *group);对str字符串起始部分不与group中任何字符匹配的字符进行计数
1.5.2 查找标记
char *strtok(char s[], const char *delim);分解字符串为一组字符串。s为要分解的字符,delim为分隔符字符(如果传入字符串,则传入的字符串中每个字符均为分割符)。首次调用时,s指向要分解的字符串,之后再次调用要把s设成NULL。
例如:strtok("abc,def,ghi",","),最后可以分割成为abc def ghi.尤其在点分十进制的IP中提取应用较多。当strtok()在参数s的字符串中发现参数delim中包含的分割字符时,则会将该字符改为\0 字符。在第一次调用时,strtok()必需给予参数s字符串,往后的调用则将参数s设置成NULL。每次调用成功则返回指向被分割出片段的指针。
char str1[]="220.179.76.98";
char *str2=".";
char *str;
//str=strpbrk(str1,str2);
str=strtok(str1,str2);
while(str!=NULL)
{
printf("%s\r\n",str);
str=strtok(NULL,str2);
}
1.6 错误信息
char *strerror(int error_number);
1.7 字符操作,分两类 都在ctype.h头文件中
字符分类
字符转换
int tolower(int ch);转换小写 int tuupper(int ch);转换大写
1.8 内存操作 都在<stdlib.h>头文件中
memcpy(拷贝内存内容)
定义函数:void * memcpy( void * dest, const void *src, size_t n );
memcpy()用来拷贝src所指的内存内容前n个字节到dest所指的内存地址上。与strcpy()不同的是,memcpy()会完整的复制n个字节,不会因为遇到字符串结束'\0'而结束。memcpy()函数可以拷贝任意类型的数据。memcpy()函数返回指向dest的指针。指针src和dest所指的内存区域不可重叠。在拷贝字符串时,通常都使用strcpy()函数;在拷贝其它数据(例如结构)时,通常都使用memcpy()函数。
memmove(拷贝内存内容)
定义函数:void *memmove(void *dest, const void *src, size_t n );
memmove()与memcpy()一样都是用来拷贝src所指的内存内容前n个字节到dest所指的地址上。不同的是,当src和dest所指的内存区域重叠时,memmove()仍然可以正确的处理,不过执行效率上会比使用memcpy()略慢些。该函数返回指向dest的指针。
memccpy(拷贝内存内容)
定义函数:void * memccpy( void *dest, const void *src, int c, size_t n );
memccpy()用来拷贝src所指的内存内容前n个字节到dest所指的地址上。与memcpy()不同的是,memccpy()会在复制时检查参数c是否出现,若是则返回dest中值为c的下一个字节地址。该函数返回指向dest中值为c的下一个字节指针。返回值为NULL表示在src所指内存前n个字节中没有值为c的字节。
memcmp(比较内存内容)
int memcmp( const void *s1, const void *s2, size_t n );
memcmp()用来比较s1和s2所指的内存区间前n个字符。字符串大小的比较是以ASCII码表上的顺序来决定。memcmp()首先将s1第一个字符值减去s2第一个字符的值,若差为0则再继续比较下个字符,若差值不为0则将差值返回。例如,字符串"Ac"和"ba"比较则会返回字符'A'(65)和'b'(98)的差值(-33)。若参数s1和s2所指的内存内容都完全相同则返回0值。s1若大于s2则返回大于0的值。s1若小于s2则返回小于0的值。
memchr(在某一内存范围中查找一特定字符)
void *memchr( const void *s, int c, size_t n );
memchr()从头开始搜寻s所指的内存内容前n个字节,直到发现第一个值为c的字节,则返回指向该字节的指针。如果找到指定的字节则返回该字节的指针,否则返回NULL。
memset(将一段内存空间填入某值)
void * memset (void *s ,int c, size_t n );
memset()会将参数s所指的内存区域前n个字节以参数c填入,然后返回指向s的指针。在编写程序时,若需要将某一数组作初始化,memset()会相当
方便。附加说明:参数c虽声明为int,但必须是unsigned char,所以范围在0到255之间。
2、动态内存分配
/动态内存分配与释放函数/
说明:
(1)
malloc()函数成功:返回所开辟空间首地址;失败:返回空指针;功能:向系统申请size字节堆的空间;
malloc(25*sizeof(int));实际一般这样写,因为在不同的机器上int类型的字节长度不一样,这些写就可以随意移植。
calloc()成功:返回所开辟空间首地址;失败:返回空指针;功能:按类型向系统申请num个size字节堆的空间;
realloc()成功:返回所开辟空间首地址;失败:返回空指针;功能:将p指向的空间变为个size字节堆的空间;
free()没有返回值,释放p指向的堆空间;
(2)
规定为void 类型,这并不是说该函数调用后无返回值,而是返回一个结点的地址,该地址的类型为void(无类型或类型不确定),即一段存储区的首址,其具体类型无法确定,只有使用时根据各个域值数据再确定。可以用强制转换的方法将其转换为别的类型。例如:
double pd = NULL;
pd = (double )calloc(10, sizeof(double));
表示将向系统申请10个连续的double类型的存储空间,并用指针pd指向这个连续的空间的首地址。并且用(double)对calloc()的返回类型进行转换,以便把double类型数据的地址赋值给指针pd。
(3)
使用sizeof的目的是用来计算一种类型的占有的字节数,以便适合不同的编译器。
(4)检查动态内存是否分配成功
由于动态分配不一定成功,为此要附加一段异常处理程序,不致程序运行停止,使用户不知所措。通常采用这样的异常处理程序段:
if (p == NULL) / 或者if(!p)/
{
printf("动态申请内存失败!\n");
exit(1); //异常退出
}
(5)这四个函数头文件均包含在中。
(6)分配的堆空间是没有名字的,只能通过返回的指针找到它。
(7)绝不能对非动态分配存储块使用free。也不能对同一块内存区同时用free释放两次,如:
free(p);
free(p);
(8)调用 free()时, 传入指针指向的内存被释放, 但调用函数的指针值可能保持不变, 因为p是作为形参而传递给了函数。严格的讲, 被释放的指针值是无效的, 因为它已不再指向所申请的内存区。这时对它的任何使用便可能会可带来问题。所以在释放一个指针指向的内存后,将该指针赋值为0,避免该指针成为野指针:
int p = (int )malloc(sizeof(int));
free(p); /释放p指向内存/
p = 0; /或者 p = NULL,释放p指向的内存后,将p指针赋值为0,避免p指针成为野指针*/
(9)malloc与calloc的区别,对于用malloc分配的内存区间,如果原来没有被使用过,则其中的每一位可能都是0;反之,如果这部分内存空间曾经被分配、释放和重新分配,则其中可能遗留各种各样的数据。也就是说,使用malloc()函数的程序开始时(内存空间还没有被重新分配)能正常运行,但经过一段时间后(内存空间已被重新分配)可能会出现问题,因此在使用它之前必须先进行初始化(可用memset函数对其初始化为0),但调用calloc()函数分配到的空间在分配时就已经被初始化为0了。当你在calloc()函数和malloc()函数之间作选择时,你需考虑是否要初始化所分配的内存空间,从而来选择相应的函数。
(10)常见错误:对NULL指针进行解引用操作、对分配的内存进行操作时超过边界、释放并非动态分配的内存、试图释放一块内存分配的内存的一部分以及一块内动态内存被释放之后被继续使用等等。
(11)有申请内存,就要free内存,否则就会引起内存泄漏
3、链表
3.1 单链表的插入
/*
** Insert into an ordered, singly linked list. The arguments are
** a pointer to the root pointer for the list, and the value to
** insert.
*/
include <stdlib.h>
include <stdio.h>
include "sll_node.h"
define FALSE 0
define TRUE 1
int
sll_insert( Node **rootp, int new_value )
{
Node *current;
Node *previous;
Node *new;
/*
** Get the pointer to the first node.
*/
current = *rootp;
previous = NULL;
/*
** Look for the right place by walking down the list
** until we reach a node whose value is greater than
** or equal to the new value.
*/
while( current != NULL && current->value < new_value ){
previous = current;
current = current->link;
}
/*
** Allocate a new node and store the new value into it.
** In this event, we return FALSE.
*/
new = (Node *)malloc( sizeof( Node ) );
if( new == NULL )
return FALSE;
new->value = new_value;
/*
** Insert the new node into the list, and return TRUE.
*/
new->link = current;
if( previous == NULL )
*rootp = new;
else
previous->link = new;
return TRUE;
}
第二版
int
sll_insert( register Node **linkp, int new_value )
{
register Node *current;
register Node *new;
/*
** Look for the right place by walking down the list
** until we reach a node whose value is greater than
** or equal to the new value.
*/
while( ( current = *linkp ) != NULL &&
current->value < new_value )
linkp = ¤t->link;
/*
** Allocate a new node and store the new value into it.
** In this event, we return FALSE.
*/
new = (Node *)malloc( sizeof( Node ) );
if( new == NULL )
return FALSE;
new->value = new_value;
/*
** Insert the new node into the list, and return TRUE.
*/
new->link = current;
*linkp = new;
return TRUE;
}