C语言字符串函数及指针
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <math.h>
void get_n_char(char s[], int n) // 从输入中获取n个字符存储到字符数组中,形参char s[]和char* s等价
{
for (int i = 0; i < n; i++)
{
s[i] = getchar();
}
// 获得n个字符,存储在s[0]至s[n-1]
s[n] = '\0'; // 在存放的字符后面放入空字符\0,使字符数组成为字符串,所以实际使用了n+1个元素位置,程序员需要确保字符数组有足够的空间存放这些元素,避免指针溢出,比如传入n为sizeof(s)-1或更小
}
void get_n_char2(char* s, int n) // 从输入中最多获取n个字符存储到字符数组中,遇到空白字符即停
{
while (n-- > 0)
{
*s = getchar();
if (isspace(*s++)) //先运算s++再运算*,s++将s的值代入表达式参与运算,之后将s的值递增,所以*s++的值等于*s,即判断新读取的字符是否为空白字符,无论true/false,s都会指向下一个元素地址/向后移动一个char长度(1byte)
break;
}
*s = '\0'; // 从输入读取到的最后一个字符存放在了s-1位置
}
void reverse_str(char* s) // 反序字符串
{
size_t len = strlen(s); // strlen()返回字符串的长度,类型为size_t无符号整数,在string.h头文件中定义,根据编译器/实现不同,实际类型可能是unsigned long
char temp;
for (size_t i = 0; i < len/2; i++)
{
temp = s[i]; // s[i]和*(s+i)等价
s[i] = s[len - 1 - i];
s[len - 1 - i] = temp;
}
}
void clear_space(void)
{
char s[40] = ""; //将字符数组初始化为空字符串,相当于{'\0'},和数组一样,如果初始化元素不足则剩余的39个字符都初始化为0,所以s中所有元素都是'\0'
char* ptr;
char* ptr_n;
const char* info = "enter empty line to quit:"; //用双引号括起来的字符串常量/字符串字面量,在内存中唯一,不要修改常量,所以用const修饰
puts(info); //puts(字符串)将字符串输出到屏幕,并在最后额外添加一个换行符,如果字符串末尾有换行符,则结果会换行两次
//gets(s); 已被C11标准废除的函数,从输入读取一行内容存储到字符数组中,由于实参是数组的首元素地址,函数中无法解决读取输入长度超过数组长度的问题,指针溢出会造成程序错误
ptr = fgets(s, 40, stdin); // fgets(字符数组,最大存储长度,读取的源) fgets会读取一行,将一行中所有字符包含换行符都存到字符数组中,如果读取的内容总长小于最大存储长度,会将包含末尾的\n全部存储并停止读取,如果读取的内容总长超过最大存储长度,则存满最大长度-1,最后一个位置放\0,读取的源可以是文件,string.h头文件中定义stdin为用户键盘输入,函数返回字符数组的首元素地址,如果读取到EOF返回NULL,而用户输入时必须以回车结束输入才能将缓存传给程序,所以每行末尾一定是\n
while (ptr != NULL&&*ptr!='\n') // NULL为空指针,该指针不会指向任何有用数据,不等于空指针就意味着fgets()函数读取到了内容并存储到s中,NULL的值为0,所以测试条件可以简写为while(ptr&&*ptr!='\n')只有当ptr指向了字符即第一个条件为true才会执行&&后面的内容,所以*ptr调用安全
{
ptr_n = strchr(s, '\n'); // strchr(字符串,字符)在字符串中从前往后查找字符,返回找到的第一个的地址,如果没找到返回NULL
if (!ptr_n) // 与ptr_n == NULL等价,等于NULL意味着字符串中没有换行符,即用户输入内容超过39长度,输入队列中有残留
while (getchar() != '\n')
continue; // 清空一行剩余内容,if语句内只有一个while语句所以省略大括号,while语句内只有一个continue所以省略大括号
while (*ptr) //*ptr!='\0'简写为*ptr,字符'\0'的值为0,\0是八进制的0,空字符'\0'是ASCII字符集的首个字符(索引0)
{
if (*ptr==' ')
{
strcpy(ptr, ptr + 1); // strcpy(目标字符数组,源字符串),将源字符串拷贝到目标字符数组指定的位置,因为实参是地址,所以通过调整第一个参数的地址来决定将字符串拷贝到数组中的什么位置,函数会将源字符串的所有内容包含空字符\0全部拷贝,不检查是否超过数组长度,需要程序员确保使用安全
continue; //将空格后面的内容前移一位来删除空格,空格删除后移过来的这个字符仍然需要判断是否为空格,所以continue再次判断
}
ptr++;
}
fputs(s, stdout); // 与fgets配对使用,fputs(字符串,输出的目的地),将字符串输出到指定文件,string.h头文件中定义stdout为屏幕/控制台,C将文件和stdin/stdout视为同一类别,fputs()不会在打印完之后额外添加换行符,所以如果字符串中没有换行符,打印之后光标仍然留在这一行的末尾
puts(info);
ptr = fgets(s, 40, stdin);
//再次给s赋值后,如果读取一行长度超过39会将之前的字符串完全覆盖,所以不会检测到之前的换行符,如果没超过39,新赋值的字符串的末尾一定是\n\0,所以检测到的一定是新的字符串的\n且一定会检测到\n,两种情况都不会出错,所以不需要在每次使用之前将数组重置
}
}
void show_string_array(void);
int get_strings(char s_ar[][40], int len);
int get_choice(void);
void show_string_array2(char* ptr_ar[], int len);
void sort_ascii(char* ptr_ar[], int len);
void sort_length(char* ptr_ar[], int len);
void sort_first_word(char* ptr_ar[], int len);
void print_by_argv(int argc, char* argv[]);
void function_about_string(void);
int main(int argc, char* argv[]) //C规定main函数可以不接收参数void,也可以接收两个参数,第一个参数argc记录命令行执行程序时传入的参数总数,第二个参数*argv[]指针数组记录每个参数字符串的地址,比如C>./program.exe see you later ,argv[0]指针元素指向"C:\program.exe",argv[1]指针元素指向"see",argv[2]指针元素指向"you",argv[3]指针元素指向"later",参数包含要执行的文件,所以argc==4,在命令行中使用双引号引起来的部分会被视为一个字符串,无论内部是否空格,且这个参数不会保留两侧的双引号
{
function_about_string();
double d1,d2;
printf("argument total : %d\n", argc);
for (int i = argc-1; i >= 0; i--) //反序打印参数列表,从最后一个argc-1打印到0
{
puts(*(argv + i)); //*(argv+i)与argv[i]等价
}
if (argc>2)
{
if (isdigit(argv[1][0])||(argv[1][0]=='-'&&isdigit(argv[1][1]))) //成为参数说明该字符串一定不为空字符串"",也就是说该字符串至少有一个字符加一个\0,所以argv[1][1]不越界,条件为true表明参数1的开头可以转换为数字
{
if (isdigit(argv[2][0]) || (argv[2][0] == '-' && isdigit(argv[2][1]))) //判断程序名后的第一个参数和第二个参数是否能转换为数字
{
d1 = atof(argv[1]); //atof(string)将字符串转换为double,如果开头是数字后面不是则转换开头部分,如果开头也不是数字则结果根据编译器实现决定,C标准未规定这种情况的结果
d2 = atof(argv[2]);
printf("%.2f^%.2f = %.2f", d1, d2, pow(d1, d2)); //计算幂,第一个参数的第二个参数次幂
}
}
}
print_by_argv(argc, argv); //将命令行参数传给函数,根据命令行参数执行打印操作
return 0;
}
void show_string_array(void)
{
// 读入最多10个字符串并提供打印和排序功能
char s_ar[10][40]; //存放10个字符串的数组
char* ptr_ar[10]; //存放10个指针的数组,每个指针用来指向字符串数组的一个字符串元素
int len;
int choice = 0;
char info[80];
puts("请输入最多10行内容(Ctrl+Z停止输入):");
len = get_strings(s_ar, 10);
while (len&&choice!=5) //条件len等价于len!=0
{
for (int i = 0; i < len; i++)
{
ptr_ar[i] = s_ar[i]; //初始化指针数组,指针数组从前往后顺序指向字符串数组
}
sprintf(info, "接收到%d行内容,可进行如下操作", len); //和printf类似,sprintf第二个参数为格式化字符串,之后的参数为待打印项,第一个参数为字符数组,函数将待打印项代入到格式化字符串中,将拼好的字符串存储到字符数组中,不打印
puts(info);
fputs("1.打印原列表\n2.以ASCII顺序打印字符串\n3.按长度递增顺序打印\n4.按字符串中第一个单词长度打印字符串\n5.退出\n请输入数字:", stdout);
switch (choice = get_choice())
{
case 2:sort_ascii(ptr_ar, len);
break;
case 3:sort_length(ptr_ar, len);
break;
case 4:sort_first_word(ptr_ar, len);
break;
case 5:
continue;
}
show_string_array2(ptr_ar, len);
}
}
int get_strings(char s_ar[][40], int len)
{
int i;
char* ptr_n;
for (i = 0; i < len; i++)
{
if (fgets(s_ar[i], 40, stdin)) //fgets读取到内容会返回第一个参数即字符数组的地址,读取到EOF返回NULL
{
if (ptr_n = strstr(s_ar[i], "\n")) //strstr(字符串1,字符串2),在字符串1中从前往后查找字符串2,返回找到的第一个的首字符的地址,没找到返回NULL
*ptr_n = '\0';
else
while (getchar() != '\n')
continue;
}
else //读取到EOF的情况,退出循环,否则连续读取十行字符串
break;
}
return i;
}
int get_choice(void)
{
char s[10];
int choice = 0;
int i;
char* end;
while (1)
{
if (fgets(s, 10, stdin))
{
if (!strpbrk(s, "\n")) // strpbrk(字符串1,字符串2),在字符串1中从前往后查找任何一个字符串2的字符,返回最先找到的字符的地址,字符串2的空字符不会被查找
while (getchar() != '\n')
continue;
for (i = 0; i<9&&isspace(s[i]); i++)
continue; // 跳过空格
//choice = atoi(&s[i]); // atoi(字符串)函数将字符串转换为数字,alpha to int,如果字符串以数字开头,函数会将开头的整数部分转换为int,忽略掉后面非整数字符的部分(如果有的话),如果字符串以非整数字符开头,C标准并未规定返回的结果,所以如果不确定能够转换成功就不要使用这个函数,同类的函数有double atof(),long atol()
choice = (int)strtol(&s[i], &end, 10); // strtol(字符串,指针的地址常量,进制)将字符串str转换为long,第三个参数是进制,可指定从2进制到36进制(使用字母a-z),第二个参数用于指向转换成功的字符部分之后的那个字符,形参char** EndPtr为指向指针的指针,比如转换"12zx",十进制,转换结果是12,第二个参数使其指向的指针变量的值指向数字12后的字符'z'的地址,即*EndPtr = &"12zx"[2],该函数想让主调函数的一个指针指向转换结束的字符的地址,即想修改主调函数的指针变量的值,而想要修改主调函数的变量,需要传入变量的地址,所以主调函数的实参为指针的地址即&end,而函数用于接收这个地址的指针变量就需要是指向指针地址的指针,所以是** EndPtr,&EndPtr是形参EndPtr的地址,EndPtr是实参&end地址,是主调函数的指针变量end的地址常量,程序通过该地址来使用end指针,类型是指针的地址,*EndPtr相当于*&end即end指针的值,是指针地址常量&end上存储的值,程序通过地址&end使用指针end,在地址&end上存储char字符的地址,所以*EndPtr或者说*&end的值是char字符的地址,是地址,在本编译器中地址是8字节,char是1字节,通过*EndPtr=&"12zx"[2]使主调函数的指针变量end指向'z'的地址,这样*end就等于'z',EndPtr的值是地址类型是&end,end的值是地址类型是&字符,两个地址类型都是8字节,但EndPtr(的值)指向指针(8字节),而end(的值)指向字符(1字节)
//这里如果传入的是&s[9]即字符串末尾的\0的地址也能正常执行,函数判断\0不是数字,将end指向\0,返回0
//同类函数double strtod(),unsigned long strtoul()
if (&s[i] == end || choice < 1 || choice>5) //如果end指向s[i]说明开头的字符就不是long十进制数字,所以一个字符都没转换,end指向了开头的字符,这种情况strtol返回0,所以可以只判断后两个条件
puts("请输入数字1-5");
else
return choice;
}
fputs("1.打印原列表\n2.以ASCII顺序打印字符串\n3.按长度递增顺序打印\n4.按字符串中第一个单词长度打印字符串\n5.退出\n请输入数字:", stdout);
}
}
void show_string_array2(char* ptr_ar[], int len)
{
while (len-- > 0)
puts(*ptr_ar++); //ptr_ar是指针数组首元素/指针的地址,该地址上存储的是字符串首元素的地址,puts()接收字符串的地址,puts(ptr_ar)错误,因为ptr_ar是指向字符串地址的指针的地址,是指针的地址,puts()接收字符串的地址,接收的是指向字符串地址的指针的值,所以应写为puts(ptr_ar[i])即*(ptr_ar+i),递增的形式为*ptr_ar++
}
void sort_ascii(char* ptr_ar[], int len)
{
if (len < 2) //如果字符串数组只有一个元素则无需排序
return;
char* temp;
int changed = 1;
for (int i = 0; changed &&i < len-1; i++) //冒泡排序
{
changed = 0;
for (int j = len-1; j > i; j--)
{
if(strcmp(ptr_ar[j-1],ptr_ar[j])>0) //strcmp(字符串1,字符串2),比较两个字符串,从首个字符开始,比较包含\0的每一个字符,当某一次比较时两个字符不相同,返回字符串1的字符-字符串2的字符的差值,根据ASCII字符集,大写字母的编码小于小写字母,即在小写字母前面,所以大写字母-小写字母为负数,而\0的编码为0,除了\0其他字符-\0都为正数,所以通过比较的结果,如果字符串1的第一个与字符串2不相同的字符在ASCII字符集中位于字符串2对应的字符的前面,则返回负数,反之返回正数
{
temp = ptr_ar[j - 1];
ptr_ar[j - 1] = ptr_ar[j];
ptr_ar[j] = temp; //当strcmp结果为正时交换两个元素
changed = 1; //从后往前逐个相邻的元素比较,j比j-1位小就交换,一轮下来会确定一个最小的元素,然后下一轮确认次最小的元素,每次发生交换就说明可能还有顺序不正确的,如果一轮下来没有发生交换说明剩下的部分已经正确排序了,结束外层循环
}
}
}
}
void sort_length(char* ptr_ar[], int len) //按长度递增排序,这里使用归并排序,将大的数组分成两部分分别排序,之后再将排序好的两部分合并排序,通过递归将数组逐渐分成每部分只包含1个元素并返回(1个元素即已排序好),从两个只包含1个元素的部分开始逐渐往更大的范围排序
{
if (len < 2) //当细分成1个元素时返回
return;
int mid = len / 2; //将长度分为两个部分
int left, right, i;
sort_length(ptr_ar, mid); //递归对两个部分分别排序
sort_length(ptr_ar + mid,len- mid); //ptr_ar + mid与&ptr_ar[mid]等价,执行完这一句说明两个部分已经分别完成了排序
char* temp[10];
for ( i = left = 0,right = mid; left < mid&&right<len; ) //实现两个部分的合并排序
{
temp[i++] = strlen(ptr_ar[left]) < strlen(ptr_ar[right]) ? ptr_ar[left++] : ptr_ar[right++]; //比较左右两个部分的最小元素(因为各自排好序了,所以是各自最左侧未使用的元素),将较小的那个元素放入临时数组中,再比较这个元素右侧的元素和另一部分最小的元素哪个更小
}
while(left<mid) //上面for循环的结束条件是两个部分中有一个部分的元素全部取完,所以此时一定有另一个部分还留有元素没有放入临时数组,在不知道是哪个部分的情况下,继续用两个循环来将剩下的元素取完
temp[i++] = ptr_ar[left++];
while(right<len)
temp[i++] = ptr_ar[right++];
for ( i = 0; i < len; i++) //将排序好的临时数组拷贝到原数组对应的区域
ptr_ar[i] = temp[i];
}
void sort_first_word(char* ptr_ar[], int len) //按首个单词的长度递增排序,这里使用随机快速排序,每次递归随机选择一个支点,将所有小于该支点的元素放到左侧,所有大于的元素放到右侧,这样即完成了对该支点的排序,之后递归执行左侧和右侧两个部分,直到所有元素都成为支点即被放到正确的位置,排序完成
{
if (len < 2) //如果字符串数组只有一个元素则无需排序
return;
char *temp, *pivot,*ptr_space;
//错误用法:char *temp, pivot,ptr_space; 声明类型char不携带*,执行时分别声明char* temp/char pivot/char ptr_space
//char ch, * ptr, str[20], * ptr_arr[20], str_arr[10][20]; 分别声明字符变量ch,字符指针ptr,字符数组str,字符指针数组ptrarr,字符数组的数组strarr,声明只共享关键字char
int ran, s1r, s2r, tlen, slen;
srand(time(NULL)); //设定随机数种子,time(NULL)返回当前时间戳,用这个必然每次都不同的时间戳设定为随机数的种子,使每次伪随机的值都不同
ran = rand() % len; //rand()函数根据种子生成伪随机数,返回int,范围从0到RAND_MAX,模len的结果为0到len-1即有效下标/索引的范围,从数组中随机选择一个元素作为支点
temp = ptr_ar[0];
ptr_ar[0] = ptr_ar[ran];
ptr_ar[ran] = temp; //将随机选取的元素放到首位
pivot = ptr_ar[0]; //将首位作为支点
//错误用法:tlen = (int)((strpbrk(pivot, " \t") || strchr(pivot, '\0')) - pivot); //使用strpbrk查找pivot指向的字符串中第一个空白字符的位置(没有查\n是因为get_strings获得函数时将\n去掉了),因为strpbrk不能查找\0即便将\0写入第二个参数中也不行,所以如果该字符串没有空白字符,strpbrk会返回NULL,使用||或运算符来处理(当strpbrk返回有效地址时不执行||右侧判断,当返回NULL时执行右侧判断),使用strchr函数查找\0的位置,所以两个函数一定有至少一个会返回有效地址,设想用两个地址差表示首个单词的长度
//但是
//C语言的逻辑运算符的值为0或1,(strpbrk(pivot, " \t") || strchr(pivot, '\0'))的值一定为1(而不是指针),1 - 指针出错
ptr_space = strpbrk(pivot, " \t");
tlen = (int)(ptr_space ? (ptr_space - pivot) : strlen(pivot));
for ( s1r = s2r = 0; ++s2r < len; ) //s1r是左侧部分(小于支点的部分)的最右侧元素,s2r是右侧部分的最右侧元素,初始化时两个部分都没有放元素,所以都是下标0
{
ptr_space = strpbrk(ptr_ar[s2r], " \t");
slen = (int)(ptr_space ? ptr_space - ptr_ar[s2r] : strlen(ptr_ar[s2r]));
if (slen<tlen) //先扩展s2r一位,比较新的这位和支点,如果小于支点则扩展s1r一位并将两个扩展的位交换
{
temp = ptr_ar[++s1r];
ptr_ar[s1r] = ptr_ar[s2r];
ptr_ar[s2r] = temp; //交换之后索引s1r的位是这一轮新扩展的位(小于支点的位),索引s2r的位是之前比较过大于支点的位
}
}
ptr_ar[0] = ptr_ar[s1r];
ptr_ar[s1r] = pivot; //将小于支点部分的最右侧的元素和支点交换,此时支点左侧都是小于支点的共有s1r位,右侧都是大于支点的共有len-s1r-1位,经过循环s2r==len
sort_first_word(ptr_ar, s1r); //当s1r==0时传入的指针依然是已经排序好的支点,但函数先判定s1r<2返回所以安全
sort_first_word(ptr_ar + s1r + 1, len - s1r - 1); //当s1r==len-1即支点右侧没有元素时,传入的指针为范围外的ptr_ar[len],C规定数组外/后首个地址为有效地址,虽然不知道该地址是用来做什么的,但是ptr_ar[len]不会报错,而函数先判断len-s1r-1<2返回所以安全
}
void print_by_argv(int argc, char* argv[])
{
puts("输入内容以EOF结束:");
char s[1024];
char ch;
int i;
for (i = 0; i < 1023&&(ch = getchar())!=EOF; i++)
{
s[i] = ch;
}
s[i] = '\0';
if (argc<2||strcmp(argv[1],"-p")==0) //比较字符串不能使用==运算符,因为这会比较地址不会比较字符,strcmp()如果两个字符串内容长度相同会返回0
{
fputs(s, stdout);
}
else if (strcmp(argv[1], "-u") == 0) //if语句中使用||或运算符,如果执行else说明两侧条件都为false,即argc>=2&&argv[1]不是-p,也就是说argv[1]一定不越界,所以不需要再次判断argc>=2,else即!(argc<2||strcmp==0),即argc>=2&&strcmp!=0,在这个范围内可直接判断argv[1]是否-u-l
{
for (char* ptr = s; *ptr; ptr++)
{
putchar(toupper(*ptr));
}
}
else if (strcmp(argv[1], "-l") == 0) //执行这个else if意味着argc>=2并且argv[1]不是-p-u
{
for (char* ptr = s; *ptr; ptr++)
{
putchar(tolower(*ptr));
}
}
}
void function_about_string(void)
{
//其他字符串相关函数
strcmp("asdf", "as"); //比较两个字符串,相同返回0,不同返回首个不同的字符的ASCII差(前减后)
printf("%d",strncmp("asdf", "as", 2)); //比较两个字符串的前2个字符,第三个参数指定比较前几个字符,这两个字符串的前两个字符相同,所以结果为0,如果参数为3,则返回'd'-'\0'
char s[80];
const char* ptr = "this is a string";
char* ptr2;
ptr2 = strcpy(s, ptr); //strcpy(dest,src)将src拷贝到dest,包含空字符,函数不会检查dest空间是否足够,需要程序员确保安全使用,将strcpy(dest,src)理解为字符串的赋值表达式,dest是左值,src是右值,如果直接写dest=src只是将字符串的地址赋值,而没有将整个字符串传过来,所以这个函数相当于字符串的赋值表达式,函数返回参数1,即左值的首元素地址
ptr2 = strncpy(s + 5, ptr, 13); //strncpy(dest,src,len)将src的前len个字符拷贝到dest,如果src长度小于len就等同于strcpy,如果大于等于len会使得末尾的\0没有拷贝过来,如果在拷贝中将目标数组原来的\0覆盖掉则需要在末尾手动补充\0,返回参数1和strcpy一样
ptr2[13] = '\0';
puts(s); //完整打印字符串
puts(ptr2); //ptr2现在指向&s[5]地址,即从第六个元素开始向后打印
ptr2 = strcat(s, ptr); //将ptr拼接到s的末尾,concatenate连接,从s的第一个\0空字符开始拷贝,拷贝包含末尾的\0,函数不检查空间是否足够,返回参数1
ptr2 = strncat(s, ptr, 4); // 拼接ptr前4位到s的末尾,如果ptr长度小于第三个参数,则效果等同strcat,将包含\0截止的所有字符拼接,如果长度大于等于第三个参数,则拷贝指定的位数,并在拷贝的内容后面一位添加\0,所以无论哪种情况,都一定会有且只有1个\0被添加到末尾,返回参数1
puts(ptr2); //ptr2现在指向s
ptr2 = strchr(s, 'f'); //从前往后查找字符f,返回第一个的地址,没找到返回NULL
ptr2 = strrchr(s, '\n'); //从字符串的末尾往前查找字符,函数接受的不是字符数组,所以是检测到第一个\0位置开始往前查找,无论这个字符数组在第一个\0后面有多少个要查找的字符都无效,只要第一个\0前面找不到这个字符,就返回NULL
ptr2 = strpbrk(s, "poiugsfdxcvb"); //在参数1字符串中从前往后查找,判断每一位字符是否在参数2字符中(不包含\0),返回发现的第一个字符地址,没找到返回NULL
ptr2 = strstr(s, "thisz"); //在参数1字符串中从前往后查找参数2字符串(不包含\0),找到则返回匹配位置首字符地址,未找到返回NULL
strlen(s); //返回字符串长度size_t类型
sprintf(s, "%d:%22s", 1, ptr); //使用格式化字符串拼接各待打印项,将拼好的字符串存储到字符数组中
fputs(s,stdout);
fgets(s, 10, stdin);
gets_s(s, 79); //C11规定可选函数,不一定适用于所有支持C11的编译器,函数从标准输入stdin/用户键盘输入读取内容拷贝到字符数组,如果读到换行符会丢弃不储存,如果读取长度小于参数2限制的最大字符数则完成拷贝并在末尾添加\0(即在原本应存储换行符的位置存储\0)返回参数1,如果读取到第二个参数所限制的最大字符数都没有读到换行符则将首位赋值\0并丢弃该行剩余的内容直至遇到换行符或文件结尾EOF并返回空指针,接着调用依赖实现的"处理函数"(或你选择的其他函数),所以函数不会修改最大字符数的后一位作为\0
atoi("123asdf");
strtol("42asdf", &ptr2, 16);
}