c语言字符函数和字符串函数的模拟实现------附带习题

简介: tolower与toupper函数是标准库提供的函数,具有平台可移植性。这意味着无论你在哪个操作系统或编译器上运行代码,该函数应该都能正常工作。而直接使用 ASCII 码加 32 并进行强制类型转换则依赖于特定的字符编码和实现。

学习网站

在学习字符函数和字符串函数之前,先给各位推荐一个c语言学习网站:cplusplus.com


--在这个网站里面我们可以查看c语言中各种函数的原型,最好先选用旧版之后再进行查阅--

字符函数

字符分类函数

作用:判断一个字符属于哪种类型的

包含头文件:<ctype.h>

它包含以下多种函数:



这些函数的使用方法都是相似的,我们来选取其中一个函数分析一下下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
int main()
{/*
  int i = islower('Q');*/
  int i = islower('q');
  printf("%d\n", i);
  return 0;
}


练习:将字符串中小写字母转成大写,其余字符不变


#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
#include<string.h>
int main()
{
  char str[] = "i am A sTUDENT";
  size_t len = strlen(str);    //strlen函数的返回值应该是无符号类型size_t
  int i = 0;
  for (i = 0; i < len; i++)
  {
    if (islower(str[i]))
    {
      str[i] = str[i] - 32;   //利用ASCII码将小写字母转换为大写字母
    }
  }
  printf("%s\n", str);
  return 0;
}

字符转换函数

tolower函数

函数原型:int tolower ( int c );

包含头文件:<ctype.h>

作用:将参数传进去的大写字⺟转小写

toupper函数

函数原型:int toupper ( int c );

包含头文件:<ctype.h>

作用:将参数传进去的小写字⺟转大写

实例:

#include <stdio.h>
#include <ctype.h>
int main()
{
    int i = 0;
    char str[] = "Test String.\n";
    char c;
    while (str[i])
    {
        c = str[i];
        if (islower(c))    //如果传入字母为小写,就利用toupper函数直接小写转大写
            c = toupper(c);
        else
            c = tolower(c);
        putchar(c);
        i++;
    }
    return 0;
}


与ACSII码方式的比较:


利用toupper函数和tolower函数转换字符大小写与利用 ASCII 码加 32 后强制类型转换字符大小写在功能上是相似的,然而它们之间存在一些区别:

  1. tolower与toupper函数是标准库提供的函数,具有平台可移植性。这意味着无论你在哪个操作系统或编译器上运行代码,该函数应该都能正常工作。而直接使用 ASCII 码加 32 并进行强制类型转换则依赖于特定的字符编码和实现。
  2. tolower与toupper函数能够处理更多字符集和本地化设置。它不仅适用于基本 Latin 字母(A-Z),还可以处理扩展字母、非拉丁字母以及其他语言中的特殊字符。这使得它们更适合国际化环境下对字符串进行大小写转换。
  3. 直接使用 ASCII 码加 32 并进行强制类型转换可能会导致溢出或未定义行为。如果输入不是大写字母,在某些情况下可能会产生错误结果或导致程序崩溃。

结论:尽管 ASCII 码加 32 的方式在某些简单场景下可行,并且比较高效,但为了保证代码的可移植性、兼容性和正确性,在大多数情况下建议使用标准库提供的 toupper函数和tolower函数

字符串函数

strlen函数

函数原型:size_t strlen (const char * str)

头文件:<string.h>

作用:求字符串长度

注意事项:

1、strlen的返回类型是无符号类型size_t

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
#include<string.h>
int main()
{
const char* str = "abcdef";
const char* str1 = "bbb";
    if(strlen(str) - strlen(str1) > 0)
    {
        printf("str1>str\n");
    }
    else
    {
        printf("str1<str\n");
    }
return 0;
}


  输出结果与我们想的好像不太一样,这是因为返回类型为两个无符号类型的3和6,无符号类型之间的运算结果肯定还是无符号数,而无符号数的定义是大于等于0的,所以str1>str。如果将两个strlen函数的返回值强制类型转换为int型结果就为str1<str了      

2、返回字符串'\0'前出现的字符个数但不包含'\0',字符串必须以 '\0'结尾

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
#include<string.h>
int main()
{ 
    char str[] = { 'a','v','b','c' };
    size_t len = strlen(str);
    printf("%zd\n", len);    //打印无符号型不能使用%d了,要用%zd
    return 0;
}


       预期输出结果为4,但是实际输出结果为74远超预期,这就是因为字符串结束后后面没有添加'\0'导致的,如果添加一个'\0',结果就会是4了。


~~注意:常量字符串系统默认后面有\0~~

练习:strlen函数模拟实现

方法一:利用指针解引用,assert断言,指针+整数

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
#include<string.h>
#include<assert.h>
size_t my_strlen(const char* str)
{
    int count = 0;
    assert(*str != NULL);   //加入assert断言,保证传入的字符串非空
    while (*str != '\0')    //当str指向的那个数字*str不是\0时就接着循环,然后count++
    {
        count++;
        str++;               //str是指针,++代表指向的地址加1,指向下一个字符
    }
    return count;            
}
int main()
{ 
    char str[] = "abcdef";
    size_t len = my_strlen(str);
    printf("%zd\n", len);
    return 0;
}

方法二:指针-指针 ==> 地址-地址,得到指针之间元素个数

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
#include<string.h>
size_t my_strlen(const char* str)
{
    const char* start = str;
    while (*str != '\0')  
    {
        str++;               
    }
    return str-start;            
}
int main()
{ 
    char str[] = "abcdef";
    size_t len = my_strlen(str);
    printf("%zd\n", len);
    return 0;
}

方法三:递归方式

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
#include<string.h>
size_t my_strlen(const char* str)
{
    if(*str != '\0')
        return  1 + my_strlen(str+1)  //这里不能写成str++,或者++str,因为++后的确是获得新地址        
    else                              //但是str指向地址仍不变
        return 0;        
}
int main()
{ 
    char str[] = "abcdef";
    size_t len = my_strlen(str);
    printf("%zd\n", len);
    return 0;
}

学前提醒:

!!不会拷贝或追加空字符!!

strcpy函数

函数原型:char* strcpy(char* destination,const char* source)

作用:拷贝字符串

头文件:<string.h>

注意事项:

1、从目标空间的起始地址拷贝,且源字符串必须以'\0'结尾

因为strcpy只有拷贝到’\0‘才会停下来,不加’\0‘就会出现访问冲突问题。

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
#include<string.h>
int main()
{
  char arr1[] = {'a','b','c'} 
    char arr2[4] = "xxx";
  strcpy(arr2, arr1);
  printf("%s\n", arr2);
  return 0;
}


2、目标空间要足够大,且目标可以修改

练习:strcpy函数模拟实现

#include <stdio.h>
#include <assert.h>
char* my_strcpy(char* dest,const char* src)   //加const修饰源字符串,保证传递过程中源
{                        //字符串指向的空间不变,char*是因为函数原型的返回类型是char*
 char* ret = dest;       //定义一个用于存放目标空间起始地址的一级指针ret
 assert(dest && src);    //保证dest和src都不为空指针
  while (*dest++ = *src++)
  {
          ;
  }
 return ret;            //返回目标空间的起始地址,stcpy功能是将源字符串的内容拷贝到目标空间,期望目标空间的内容发生变化,所以返回目标空间的起始地址,方便观察目标空间的数据
}
int main()
{
  char arr1[] = "abcdef";
  char arr2[20] = { 0 };
  printf("%s\n",my_strcpy(arr2,arr1));
  return 0;
}


常量字符串

①常量字符串是指在程序中无法被修改的字符串

②通常由双引号或单引号包围,例如:“Hello, World!”或'42'

常量字符串有效字符个数 < 规定字符个数,因为还要为\0保留位置

比如:char arr2[4] = ”xxx“;


由于常量字符串无法修改,所以在使用*p = 'w'时,系统会出现访问权限冲突问题,但是在程序运行之前系统是不会报错的,所以这时候我们可以增加一个const修饰*p从而使其从源头上就不能修改*p的值,此时再书写*p = 'w'时系统就会系统就会报错:



strcat函数

函数原型:char* strcat(char* destination,const char* source)

作用:字符串追加

头文件:<string.h>

注意事项:

1、从目标空间的第一个\0处开始追加,同时覆盖掉该\0,且源字符串必须以'\0'结尾

追加位置规定从目标字符串的\0处开始,但\0可以人为添加,故这里说从第一个\0处开始追加

#include <stdio.h>
#include <string.h> 
int main()
{
  char arr1[20] = "hello \0xxxxxxxxx";
  char* p = "world";
  strcat(arr1, p);
  printf("%s\n", arr1);
  return 0;
}


2、目标空间要足够大,且目标可以修改

3、不能实现对自身的追加

char* my_strcat(char* dest, const char* src)
{
  char* ret = dest;
  assert(dest && src);
  //找到目标空间的'\0'
  while (*dest != '\0')
  {
    dest++;
  }
  //拷贝数据
  while (*dest++ = *src++)
  {
    ;
  }
  return ret;
}
int main()
{
  char arr1[20] = "abcdef";
  my_strcat(arr1, arr1);
  printf("%s\n", arr1);
  return 0;
}


通过画图来理解一下,刚开始的时候dest和src是这样的:



dest在经历了while (*dest != '\0')后:


然后开始执行while (*dest++ = *src++)后:



如果arr1的空间足够大,src是永远也不可能追上dest的,同时当dest指向数组边界时,由于src还没有追上dest,就会导致数组越界访问了

练习:strcat函数模拟实现

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{
  char* ret = dest;
  assert(dest && src);
    //找到目标空间的'\0'
  while (*dest != '\0')
  {
    dest++;
  }
    //拷贝数据
  while (*dest++ = *src++)
  {
    ;
  }
  return ret;
}
int main()
{
  char arr1[20] = "hello \0xxxxxxxxx";
  char* p = "world";
  my_strcat(arr1, p);
  printf("%s\n", arr1);
  return 0;
}

strcmp函数

函数原型:int strcmp (char* str1,char* str2)

作用:比较两个字符串,依据比较结果返回整数

头文件:<string.h>

注意事项:

1、第一个字符串大于第二个字符串,返回大于零的整数

2、第一个字符串等于第二个字符串,返回等于零的整数

3、第一个字符串小于第二个字符串,返回小于零的整数

4、只能返回第一次在同一位置上两个字符不相等时,两字符的ASCII码差值,并不会返回两个字符串ACSII码的整体差值

练习:strcmp函数模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strcmp(const char* s1,const char* s2)   //比较时str1和str2不能被改变
{
  assert(s1 != NULL);
  assert(s2 != NULL);
  while (*s1 == *s2)     //如果两个字符串在同一位置的字符上相同,进入循环
  {
    if (*s1 == '\0')    //如果同时s1在该位置上的字符为'\0',证明两个数完全相等,返回零
      return 0;
    s1++;               //++继续向后面逐位比较
    s2++;
  }
  return *s1 - *s2;       //如果此时s1和s2指向的字符不同,就返回两个字符在ASCII码中的差值
}
int main()
{
  int ret = my_strcmp("abg", "abc");
  printf("%d\n", ret);
  return 0;
}

思考:

      我们在上面学习了strcpy函数、strcat函数、strcmp函数,我们发现它们在拷贝、追加、比较的时候是直接将提供的字符串全部进行操作,并没有明确规定到底要操作多少内容, 我们统称这类函数是不受长度限制的字符串函数,下面我们就来学习它们三个的升级版,受长度限制的字符串函数:strncpy函数、strncat函数、strncmp函数。

strncpy函数

函数原型:char* strcpy(char* destination,const char* source,size_t num)

作用:拷贝字符串(将source字符串中num个字节的字符拷贝给destination字符串)

头文件:<string.h>

注意事项:

1、从目标空间起始地址开始拷贝,源字符串长度 >= num,有多少拷贝多少

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
int main()
{
  char arr1[20] = "xxxxxxxxxx"; 
  char arr2[] = "abcdef";
  strncpy(arr1, arr2, 3);   
  printf("%s\n", arr1);     
  return 0;
}

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
int main()
{
  char arr1[20] = "xxxxxxxxxx";
  char arr2[7] = "abcdef";
  strncpy(arr1, arr2, 7);
  printf("%s\n", arr1);
  return 0;
}

2、源字符串长度 < num,不足部分自动补'\0'

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
int main()
{
  char arr1[20] = "xxxxxxxxxx"; 
  char arr2[] = "abc";
  strncpy(arr1, arr2, 5);    
  printf("%s\n", arr1);     
  return 0;
}

练习:strncpy函数模拟实现

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<errno.h>
char* my_strcpy(char* dest,const char* src,size_t num)   
{                        
  int i = 0;
  for (i = 0; *(src + i) && i < num; i++) 
  {                   
    *(dest + i) = *(src + i);     
  }
  while (i < num)   
  {
    *(dest + i) = '\0';   //由于是自定义函数末尾加'\0'需要依靠循环人为添加,系统不会自己添加
    i++;
  }
  return dest;
}
int main()
{
  char arr1[20] = "abc\0xxxxxxx";
  char arr2[] = "def";
  size_t len = strlen(arr2);
  printf("%s\n",my_strcpy(arr1,arr2,5));  
  return 0;
}

strncat函数

函数原型:char* strcat(char* destination,const char* source,size_t num)

作用:字符串追加(将source字符串中num个字节的字符在destination字符串第一个\0后追加)

头文件:<string.h>

注意事项:

1、源字符串长度不论与num的大小关系为多少,追加完成后末尾必定补'\0'

源自符串长度大于num:

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
int main()
{
  char arr1[20] = "abc\0xxxxxxx";   //目标空间从/0后开始追加
  char arr2[] = "defghi";
  strncat(arr1, arr2, 5);
  printf("%s\n", arr1);
  return 0;
}

源自符串长度等于num: 

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
int main()
{
  char arr1[20] = "abc\0xxxxxxx";   //目标空间从/0后开始追加
  char arr2[] = "defghi";
  strncat(arr1, arr2, 7);
  printf("%s\n", arr1);
  return 0;
}

源自符串长度小于num:

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
int main()
{
  char arr1[20] = "abc\0xxxxxxx";
  char arr2[] = "def";
  strncat(arr1, arr2, 5);
  printf("%s\n", arr1);
  return 0;
}


练习:strncat函数模拟实现

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<errno.h>
char* my_strncat(char* dest, const char* src,size_t num)
{
  char* ret = dest;
  assert(dest && src);
  while (*dest != '\0')
  {
    dest++;
  }
  for (int i = 0; i < num; i++)
  {
    *dest = *src;
    dest++;
    src++;
  }
  return ret;
}
int main()
{
  char arr1[20] = "hello \0xxxxxxxxx";
  char* p = "world";
  my_strncat(arr1, p,3);
  printf("%s\n", arr1);
  return 0;
}

strncmp函数

函数原型:int strcmp (char* str1,char* str2,size_t num)

作用:比较两个字符串,依据比较结果返回整数

头文件:<string.h>

注意事项:

1、比较两个字符串中前num个字符是否相等,相比于strcmp函数它能规定比较范围,返回值情况与strcmp函数相同,小于零证明s1小于s2,大于零证明s1大于s2,等于零证明s1等于s2

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
int main()
{
  char arr1[20] = "abcdef";
  char arr2[] = "abcdge";
  int ret = strncmp(arr1, arr2, 5);
  printf("%d\n", ret);
  return 0;
}

练习:strncmp函数模拟实现

       这次的模拟并不是简单的模拟了,我们让它实现了一些功能,只依靠随机返回的大于小于或者等于这些值看起来没多大用处,我们让他能返回两字符串前num个字符ASCII码总值的差值,这样既能起到一定的实际用处又能跟原来一样通过返回的值判断两个字符串是否相同


#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<errno.h>
int my_strncmp(const char* s1, const char* s2,size_t num)  //比较时str1和str2不能被改变
{
  assert(s1 != NULL);
  assert(s2 != NULL);
  int sum = 0;
  for (int i = 0; i < num; i++)
  {
    if (*s1 == *s2)
    {
      if (*s1 == '\0')
        return 0;
    }
    else
    {
      sum += *s1 - *s2;
    }
    s1++;
    s2++;
  }
  return sum;
}
int main()
{
  int ret = my_strncmp("zqp", "yyf",2);
  printf("%d\n", ret);
  return 0;
}

strstr函数


函数原型:char * strstr ( const char *str1, const char *str2 )

作用:返回str2在str1第一次出现的位置,如果没有出现,就返回空指针NULL

头文件:<string.h>

注意事项:

练习:strstr函数模拟实现

~~请点开所有代码,以便查看全部注释~~

const char* my_strstr(const char* str1, const char* str2)//str1指向arr1数组的首字符地址,str2指向arr2数组的首字符地址
{
  //判断str1和str2是否为空
  assert(str1);
  assert(str2);
  const char* cp = str1;  //cp用于记录可能找到匹配的字符串的起始位置,当cp位于有可能匹配成功的位置,这时候就轮到s1和s2开始动了
  const char* s1 = NULL;  
  const char* s2 = NULL;  
  //如果子串是空字符串,直接返回str1
  if (*str2 == '\0')
    return str1;
  while (*cp)       //如果str1非空,那么就会从str1的第一个字符开始
  {
    s1 = cp;    //起始时s1也指向arr1数组首字符地址
    s2 = str2;    //起始时s2也指向arr2数组首字符地址,同时也是防止在可能找到匹配字符的起始位置之后的几个位置寻找后并未找到可以完全匹配的内容,方便下一次的比对,总之就是为了方便每次匹配失败后的再次匹配
    while (*s1 == *s2 && *s1 && *s2)  //如果s1和s2指向的字符不相等那么不会进入循环
    {
      s1++;    //如果s1和s2指向内容相等且s1和s2均不为'\0'时,证明还可以继续向后走所以++
      s2++;
    }
    if (*s2 == '\0')  //当子字符串此时指的位置为'/0'时,证明此时子字符串已经读取完了
      return cp;   
    cp++;  //如果一直找不到cp一直++,最后*cp就会指向arr1数组的'\0'了,循环结束返回一个空值
  }
  return NULL;
}
int main()
{
  char arr1[] = "abbbcdef";
  char arr2[] = "bbc";
  char* ret = my_strstr(arr1,arr2);
  if (ret != NULL)
    printf("%s\n", ret);
  else
    printf("找不到\n");
  return 0;
}

tips:return char* 类型的指针后,系统会自动将以该指针指向的位置为起始位置,然后自动向后面访问整个字符串,这也就是为什么return cp后虽然是一个地址但是最后打印的内容是bbcdef了

可能对模拟实现还有疑问,下面我们通过画图的办法模拟指针的运动帮助大家理解:

1、第一次循环前


2、第一次循环后,第二次循环前


3、第二次循环内部过程


4、第二次循环后,第三次循环前


5、第三次循环过程


最后一次while (*s1 == *s2 && *s1 && *s2)后,s2++满足*s2 == '\0',此时返回存储的首地址

strtok函数

函数原型:char * strtok ( char * str, const char * sep)

作用:分割字符串

头文件:<string.h>

注意事项:

1、sep指向由多个分隔符组成的字符串;str指向待分割字符串;

2、找到待分割字符串中的首个分隔符后用\0代替,此时\0与前面的一段字符都会被看作一个标记

3、函数的第二个参数一直都是sep即由分隔符组成的字符串

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
  char arr[] = "192.168.6.111";
  char* sep = ".";
  char* str = NULL;
  for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
  {
    printf("%s\n", str);
  }
  return 0;
}


4、被分割的字符串一般都先放在创建的临时数组中

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<errno.h>
int main()
{
  char arr[] = "192.168.12.102";
    char buf[30] = {0};           //将字符串拷贝给buf字符数组,然后想怎么改就怎么改了,最后结果                        
    strcpy(buf,arr);              //也不会影响arr字符数组里的字符串
  char* sep = ".";   //.为分隔符
  char* s = strtok(buf,sep);
  printf("%s\n", s);
  return 0;
}

5、strtok函数第一次传入的都是非空指针即原始字符串作为第一个参数传递给函数,但是第二次之后传入的都是空指针,这样函数会从上一次找到标记位置的下一个字符开始查找下一个标记

练习:strtok函数的使用(for循环分割很长内容)

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<errno.h>
int main()
{
    char arr[] = "yanhyf@yeah.net";
    char buf[60] = { 0 };
    strcpy(buf, arr);
    char* p = "@.";     //.为分隔符
    char* r = NULL;     //使用前先置为空
    for (r = strtok(buf, p); r != NULL; r = strtok(NULL, p))
    {                             //(初始化部分 ; 循环判断条件 ; 再次调用)
        printf("%s\n", r);
    }
    return 0;
}

streeor函数

函数原型:char * strerror ( int errnum)

作用:返回错误码(类似于http中的整数404)所对应的错误信息字符串的起始地址

头文件:<errno.h>

注意事项:

1、c语言每个错误码都有其所对应的错误信息字符串


#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<errno.h>
int main()
{
  int i = 0;
  for (i = 0;i<10; i++)
  {
    char* ret = strerror(i);
    printf("%d : %s\n", i, ret);
  }
  return 0;
}

    strerror函数可以把参数部分错误码对应的错误信息的字符串地址返回来,在不同的系统和C语⾔标准库的实现中都规定了⼀些错误码,⼀般是放在 <errno.h> 这个头⽂件中说明的,C语⾔程序启动的时候就会使⽤⼀个全局变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表⽰没有错误,当我们在使⽤标准库中的函数时发⽣了某种错误,就会将对应的错误码存放在errno中,⽽⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是有对应的错误信息的。strerror函数就可以将错误码对应的错误信息字符串的地址返回,从而让我们开到具体的错误信息。  

2、当库函数调用失败的时候,会将错误码记录到errno这全局个变量中

#define _CRT_SECURE_NO_WARNINGS 1
#include<ctype.h>
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<errno.h>
int main()
{
  FILE* pf =  fopen("add.txt", "r");
  if (pf == NULL)    //当pf指向的文件不存在时,系统就会自动感受到错误从而生成错误码并将错误码交给全局变量errno
  {
    /*printf("打开文件失败,失败的原因:%s\n", strerror(errno));*/
    perror("fopen");   
    printf("fopen:%s\n", strerror(errno));
    return 1;
  }
  else
  {
    printf("打开文件成功\n");
    //......
  }
  return 0;
}

perror的作用就相当于下一行的printf("fopen:%s\n", strerror(errno)) 二者打印结果一样:


对fopen()函数的简单理解:

fopen()函数会获取文件信息,包括文件名、文件状态、当前读写位置等,并将这些信息保存到一个 FILE 类型的结构体变量中,然后将该变量的地址返回。

如果希望接收 fopen() 的返回值,就需要定义一个 FILE 类型的指针。例如:

FILE* fp = fopen("demo.txt", "r");

表示以“只读”方式打开当前目录下的 demo.txt 文件,并使 fp 指向该文件,这样就可以通过 fp 来操作 demo.txt 了。fp 通常被称为文件指针

再来看一个例子:

FILE* fp = fopen("D:\\demo.txt", "rb+");

表示以二进制方式打开 D 盘下的 demo.txt 文件,允许读和写。


3、在创建文件时,记得将文件夹 "查看" 里面的"文件扩展名"勾选上


目录
打赏
0
0
0
0
4
分享
相关文章
一文彻底搞清楚C语言的函数
本文介绍C语言函数:函数是程序模块化的工具,由函数头和函数体组成,涵盖定义、调用、参数传递及声明等内容。值传递确保实参不受影响,函数声明增强代码可读性。君志所向,一往无前!
11 1
一文彻底搞清楚C语言的函数
|
2月前
|
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
75 16
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
42 3
【C语言程序设计——函数】分数数列求和1(头歌实践教学平台习题)【合集】
if 语句是最基础的形式,当条件为真时执行其内部的语句块;switch 语句则适用于针对一个表达式的多个固定值进行判断,根据表达式的值与各个 case 后的常量值匹配情况,执行相应 case 分支下的语句,直到遇到 break 语句跳出 switch 结构,若没有匹配值则执行 default 分支(可选)。例如,在判断一个数是否大于 10 的场景中,条件表达式为 “num> 10”,这里的 “num” 是程序中的变量,通过比较其值与 10 的大小关系来确定条件的真假。常量的值必须是唯一的,且在同一个。
25 2
【C语言程序设计——函数】回文数判定(头歌实践教学平台习题)【合集】
算术运算于 C 语言仿若精密 “齿轮组”,驱动着数值处理流程。编写函数求区间[100,500]中所有的回文数,要求每行打印10个数。根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码。如果操作数是浮点数,在 C 语言中是不允许直接进行。的结果是 -1,因为 -7 除以 3 商为 -2,余数为 -1;注意:每一个数据输出格式为 printf("%4d", i);的结果是 1,因为 7 除以 -3 商为 -2,余数为 1。取余运算要求两个操作数必须是整数类型,包括。开始你的任务吧,祝你成功!
56 1
|
10月前
|
C 语言函数完全指南:创建、调用、参数传递、返回值解析
函数是一段代码块,只有在被调用时才会运行。 您可以将数据(称为参数)传递给函数。 函数用于执行某些操作,它们对于重用代码很重要:定义一次代码,并多次使用。
254 3
C语言函数返回值详解
本文详细解析了C语言中函数返回值的概念与应用。从函数的基本定义入手,深入探讨了不同类型返回值的作用及意义,并提供了实用的编程示例,帮助读者更好地理解和使用函数返回值。通过本文,你将掌握如何有效利用返回值优化代码结构与功能实现。
C语言的函数返回值和指针
C|函数返回值(区分各类值)和指针(区分各类存储空间)的细节
C语言---函数---知识点总结(三)------函数的返回值类型
C语言---函数---知识点总结(三)------函数的返回值类型
C语言中向函数传递值和从函数返回值的技术解析
C语言中向函数传递值和从函数返回值的技术解析
98 0