【问题标题】:Parameter as pointer not being correctly incremented作为指针的参数未正确递增
【发布时间】:2021-01-01 10:45:30
【问题描述】:

我有一个函数可以计算字符串中元音和辅音的数量:

void CountVowelsConsonants(char* str, int *vowels, int *consonants){
    size_t size=strlen(str);
    //size-1 because of the '\n' at the end of the string when pressing enter
    for (int i = 0; i < size ; ++i) {
        char c = str[i];
            if(c=='A' || c== 'E' || c=='I' || c== 'O' || c=='U' ||
                    c=='a' || c== 'e' || c=='i' || c== 'o' || c=='u'){
                *vowels++;
            }
        }
    *consonants= size-1 - *vowels;
    }

但是当我调用这个函数时,例如:

int vowels;
int consonants;
CountVowelsConsonants("abc", &vowels, &consonants);

它返回:

Vowels: -858993460
Consonants: 858993463

代替:

Vowels: 1
Consonants: 2

我想知道为什么会这样。

我有一个阶乘函数,它具有类似的指针实现并且可以正常工作:

void factorial(int n, int*fac){
    *fac=1;
    for (int i = 2; i <=  n; ++i) {
        *fac*=i;
    }
}

在阶乘函数中访问事实指针将正确地改变值。唯一的区别是,在CountVowelsConsonants 函数中,我是递增 1 而不是相乘。

但是,如果我访问 CountVowelsConsonants 函数中的元音指针来增加它会在计数中产生错误。

我的IDE(CLion)会把*vowels中的*变灰,说明里面的指针操作符 *vowels++; 没用。

关于为什么会这样的任何想法?

【问题讨论】:

    标签: c loops for-loop pointers parameter-passing


    【解决方案1】:

    代替:

    *vowels++;
    

    相当于这个:

    *vowels;            // this does nothing, that's what your IDE was telling you
    vowel = vowels + 1; // this increments the vowels pointer,
                        // which is also pretty useless here
    

    你需要这样写:

    (*vowels)++;
    

    这等价于你真正想要的:

    *vowels = *vowels + 1;
    

    ++ 运算符的 precedence 为 1,而 *(取消引用)运算符的优先级为 2。

    【讨论】:

      【解决方案2】:

      首先,如果变量vowelsconsonants 在main(或其他函数)中声明,那么它们不会被初始化并且具有不确定的值。你需要写

      int vowels = 0;
      int consonants = 0;
      CountVowelsConsonants("abc", &vowels, &consonants);
      

      第一个函数参数应该用限定符 const 声明,因为传递的字符串在函数中没有改变。

      void CountVowelsConsonants( const char* str, int *vowels, int *consonants);
      

      顺便说一下,第二个和第三个参数的类型应该是size_t

      void CountVowelsConsonants( const char* str, size_t *vowels, size_t *consonants);
      

      在函数内调用strlen对传递的字符串是低效的。

      size_t size=strlen(str);
      

      循环内的变量i应声明为size_t类型。

      for ( size_t i = 0; i < size ; ++i) {
      

      但如果不调用函数strlen,循环可能看起来更简单

      for ( ; *str != '\0'; ++str ) {
          char c = *str;
          if(c=='A' || c== 'E' || c=='I' || c== 'O' || c=='U' ||
                  c=='a' || c== 'e' || c=='i' || c== 'o' || c=='u'){
              ++*vowels;
          }
          else {
             ++*consonants;
          }
       }
      

      这个表达式

      *vowels++;
      

      不正确。相当于表达式

      *( vowels++ );
      

      即指针本身递增。

      你应该写

      ++*vowels;
      

      这个表达式

      *consonants= size-1 - *vowels;
      

      也是不正确的。

      如果您使用的是通过调用strlen 获得的变量size,那么您必须编写

      *consonants= size -*vowels;
      

      注意,如果传入的字符串包含空格或标点符号,函数将无法正常工作。

      所以在函数中声明循环至少如下方式会更正确

      #include <ctype.h>
      
      //...
      
      for ( ; *str != '\0'; ++str ) {
          char c = *str;
      
          if ( isalpha( ( unsigned char )c ) ) {
              if(c=='A' || c== 'E' || c=='I' || c== 'O' || c=='U' ||
                      c=='a' || c== 'e' || c=='i' || c== 'o' || c=='u'){
                  ++*vowels;
              }
              else {
                 ++*consonants;
              }
          }
       }
      

      【讨论】:

      • 你的回答超级完整。我从中学到了很多。如果您不介意,我有两个问题:为什么 size_t 而不是 int(我认为这是因为它们是正值?)以及为什么在这种情况下调用 strlen 效率低?
      • @Skatinima 函数 strlen 的返回类型为 size_t。 int 类型的对象可能不够大,无法包含 size_t 类型的对象的任何值。调用 strlen 您是第一次遍历字符串。然后在 for 循环中第二次遍历相同的字符串。
      • @Skatinima 请注意,在主要元音和辅音中也应具有 size_t 类型,如果您使用函数 printf 输出它们,则必须使用转换说明符 %zu。
      猜你喜欢
      • 2013-07-15
      • 2016-09-30
      • 1970-01-01
      • 1970-01-01
      • 2012-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多