C:带指针的字符串操作

  • 本文关键字:字符串 操作 指针 c
  • 更新时间 :
  • 英文 :


我来自JS,我仍然在努力理解指针的概念,为此,我创建了一个函数来小写字符串。

char    *ft_strlowcase(char *str)
{
int i;
i = 0;
while (str[i])
{
if (str[i] >= 'A' && str[i] <= 'Z')
{
str[i] = str[i] + 32;
}       
i++;
}
return (str);
}

现在,我想测试它,在我的逻辑中,我可以传递一个指针到我的函数,像这样:

int main(void)
{   
//char  str1[] = "AbCdEfGhI"; //WORKING
//char  *str1 = "AbCdEfGhI"; //NOT WORKING

//char   *str1; //NOT WORKING
//str1[] = "AbCdEfGhI"; //NOT WORKING
char    *str1; //NOT WORKING
str1 = "AbCdEfGhI"; //NOT WORKING

printf("Lowercase : %sn", str1);
ft_strlowcase(&str1);
printf("Uppercase : %sn", str1); 
}

但是它不起作用,我能使它起作用的唯一方法,就是传递一个在一行中声明的数组。我错过了什么?我可以使它与指针语法工作,没有任何复杂的函数(memloc…)?

您呼叫的正确版本是ft_strlowcase(str1)。然而,这仍然会导致未定义的行为,因为str1 = "AbCdEfGhI"创建的字符串文字对象正在被修改。字符串字面值是程序图像的一部分;修改字符串字面值是事实上的自修改代码,其行为没有由ISO c定义。

这里动态语言和C语言没有太大区别;ANSI Common Lisp在修改文字对象方面也是如此。

顺便说一下,你的C编译器应该警告你ft_strlowcase(&str1)调用是病态的:它传递了一个char **指针到一个期望char *的函数。

现在关于这个:

//char  str1[] = "AbCdEfGhI"; //WORKING

可以,有两个(半)原因。

  1. 首先,"AbCdEfGhI"在这里不再是一个文字对象,而只是一个初始化语法。对象是str1数组,""AbCdEfGhI"指定它的大小和初始内容。这个str1数组不是字符串字面值;它是可变的。很好地定义了像str[0]++这样的操作。

  2. 因为str1是一个数组,表达式&str1产生一个指向数组的指针。价值。但是这个指针指向的地址和那个数组的第一个字符是一样的。也就是说,因为str是一个数组,所以str&str[0]&str都是同一个指针。前两个变量的类型是char *,而&str的类型是char (*)[10]:指针指向一个包含10个char的数组。表达式ft_strlowcase(&str1)仍然需要诊断,这使得程序没有定义:您要求编译器将一个指针类型转换为不兼容的类型,而不进行强制类型转换。但是,如果编译器只是发出诊断,然后提供转换,就好像有强制转换一样,那么您将得到明显正确的行为。您需要ft_strlowcase(str1)(非常优选)或ft_strlowcase((char *) &str1)(提供类型转换,因此不需要诊断)。如果需要诊断的程序被翻译并执行,那么它们就会有未定义的行为!

最后,ft_strlowcase函数是冗长的。在其他可能性中,更习惯的C代码看起来像这样:

char *ft_strlowcase(char *str)
{
for (char *ptr = str; *ptr; ptr++) {
if (*ptr >= 'A' && *ptr <= 'Z')
*ptr += 32;
}
return str;
}

信不信由你,对于有经验的C程序员来说更容易读懂,因为它把几个容易理解的、常用的习语浓缩成一个简洁的集合。

在您的原始代码中,这是特别应该避免的:

int i;
i = 0;

无缘无故地拒绝了一个定义初始化变量的明显机会:

int i = 0;

初始化总是优先于赋值。它们是不同的。初始化意味着对象"诞生"。用一种价值走进这个世界;在它的程序可见存在的任何一点上都没有一个值。在C语言中,我们可以定义一个局部变量对象而不初始化它,这将使它成为"不确定值"。这种习惯造成了使用不确定值对象的风险,这是一种未定义行为。

最新更新