C语言 有困难的指针和字符串,乱码打印

lg40wkob  于 2023-10-16  发布在  其他
关注(0)|答案(5)|浏览(115)

这是一个使用libsodium的密码(骰子)生成器。
get_word函数应该将选定的单词(字符串)写入作为参数char *word传递的指针,但它不起作用。相反,打印的是乱码-随机字符。当它打印出 * 第一个单词 * 时,您可以看到这一点。

#include <stdio.h>
#include <string.h>
#include <sodium.h>

void roll_dice(char *combo) {
    uint32_t dice1, dice2, dice3, dice4, dice5;
    uint32_t upper = 6;

    dice1 = randombytes_uniform(upper) + 1;
    dice2 = randombytes_uniform(upper) + 1;
    dice3 = randombytes_uniform(upper) + 1;
    dice4 = randombytes_uniform(upper) + 1;
    dice5 = randombytes_uniform(upper) + 1;

    sprintf(combo, "%d%d%d%d%d", dice1, dice2, dice3, dice4, dice5);
}

int get_word(char *combo, char *word) {
    FILE *stream;
    char line[20];
    char *wordline;

    stream = fopen("/usr/local/etc/passwiz/eff_large_wordlist.txt", "r");

    if (NULL == stream) {
        printf("Failed to open wordlist\n");
        return -1;
    }

    while (fgets(line, sizeof(line), stream)) {
        wordline = strstr(line, combo);

        if (wordline > 0) {
            // TODO: slice the string to obtain just the word
            //wordline = strtok(wordline, "\t");
            //wordline = strtok(wordline, "\t");

            *word = wordline;
        }
    }

    fclose(stream);
}

int main(void) {
    if (sodium_init() < 0) {
        return 1;
    }

    char combo[6];
    roll_dice(combo);
    printf("Dice numbers: %s\n", combo);

    char word1[20];
    char *ptr = &word1;

    if (get_word(combo, ptr) < 0) {
        return 1;
    }

    printf("First word: %s\n", word1);

    // TODO: get 6 words
}

我使用的单词表来自EFF。参见here

11111   abacus
11112   abdomen
11113   abdominal
11114   abide
11115   abiding
11116   ability
11121   ablaze
11122   able
11123   abnormal
11124   abrasion
11125   abrasive

up to 66666

我运行它时看到的标准输出示例:

Dice numbers: 32345
First word: 3#@$#

编辑:对不起,我忘了说我从编译器得到了很多警告。警告本身并不能帮助我理解我做错了什么-我真的是C的新手。我回家后会贴警告的。

fykwrbwg

fykwrbwg1#

已经有很多关于char **char *之间差异的反馈,不需要重复。
虽然根据需要多次打开/关闭函数中的文件似乎很方便,但这确实给操作系统带来了负担,并且会减慢执行速度。此外,平均而言,程序将读取1/2的方式进入文件之前,找到正在寻找的标记。重复此6次(6个单词)将像阅读整个文件3次。浪费。
由于文件并不大,因此完整地加载一次似乎是有意义的。然后,它可以重复扫描感兴趣的目标。(此代码假定文件中的最后一个字符将是最后一个单词末尾的'\n'。
像@NoDakker一样,我不使用Sodium,所以我设计了一种替代方法来生成5个ASCII数字的字符串,代表5卷骰子。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>

// Load file contents to alloc'd memory, preserving end-of-buffer
// return pointer to buffer to be free'd by caller
char *loadFile( char *fname ) {
    struct stat finfo;

    if( stat( fname, &finfo ) != 0 ) {
        fprintf( stderr, "Cannot see '%s'\n", fname );
        exit( EXIT_FAILURE );
    }

    FILE *fp = fopen( fname, "rb" );
    if( !fp ) {
        fprintf( stderr, "Cannot open '%s'\n", fname );
        exit( EXIT_FAILURE );
    }

    char *buf = malloc( finfo.st_size + 1 ); // NB: +1
    if( !buf ) {
        fprintf( stderr, "Malloc() failed\n" );
        exit( EXIT_FAILURE );
    }

    size_t nread = fread( buf, 1, finfo.st_size, fp );
    if( nread != (size_t)finfo.st_size ) {
        fprintf( stderr, "Read incomplete\n" );
        exit( EXIT_FAILURE );
    }

    fclose( fp );

    buf[ nread ] = '\0'; // extra byte set to NULL (for str functions)

    return buf;
}

void roll_dice( char *combo ) {
    for( int i = 0; i < 5; i++ )
        *combo++ = '1' + rand() % 6;
    *combo = '\0'; // terminate
}

int main( void ) {
    srand( time( NULL ) );

    char *all = loadFile( "eff_large_wordlist.txt" );

    for( size_t i = 0; i < 6; i++ ) {
        char combo[ 5 + 1 ];
        roll_dice( combo );
        char *p = strstr( all, combo );
        if( p ) {
            p += 6;
            do putchar( *p ); while( *p++ != '\n' ); // non-destructive output
        }
    }

    free( all );

    return 0;
}

输出量:

prelude
herbal
pennant
cash
empirical
secluded
i7uq4tfw

i7uq4tfw2#

根本原因是

char word1[20];
char *ptr = &word1;

应当

char* ptr = word1;

由于ptr指向word1的地址,它将开始处理所有类型的数据。一旦你解决了这个问题,下一个问题是

*word = wordline;

其目的可能是将SQL Server的内容复制到Word中,因此,

strcpy(word, wordline);

如果你打开编译器警告,它应该告诉你这些。

sg24os4d

sg24os4d3#

我很惊讶编译器没有因为不匹配的类型而向你抱怨。
在函数 get_word() 中:

*word = wordline;

在指针字所指向的位置将类型(char*)(指针)赋给类型(char)。我想你的意思是:

strcpy(word, wordline);
np8igboo

np8igboo4#

一大堆问题。

  1. *word = wordline;
    你用指针分配整数。可能你想修改传递给函数的指针。你需要将这个参数声明为指针到指针来实现它。
int get_word(char *combo, char **word)

1.即使你改变它。分配的指针将引用本地数组,当函数返回时,该数组将停止存在。
1.即使你把line设为静态,你也会打印出word1,它不会通过赋值指针进行修改。
解决方案:
你需要将字符串复制到参数中(保持其类型为char *):
而不是*word = wordline;-> strcpy(word, wordline);

xyhw6mcr

xyhw6mcr5#

与其他答案和注解一样,我注意到编译器警告,这些警告表明您试图引用“指向指针的指针”,而不是引用字符数组名称,这实际上是指向数组的指针。

/home/craig/C_Programs/Console/Sodium/main.c|44|warning: assignment to ‘char’ from ‘char *’ makes integer from pointer without a cast [-Wint-conversion]|

/home/craig/C_Programs/Console/Sodium/main.c|58|warning: initialization of ‘char *’ from incompatible pointer type ‘char (*)[20]’ [-Wincompatible-pointer-types]|

/home/craig/C_Programs/Console/Sodium/main.c|49|warning: control reaches end of non-void function [-Wreturn-type]|
||=== Build finished: 0 error(s), 3 warning(s) (0 minute(s), 0 second(s)) ===|

回顾一下程序的预期用途,下面是代码的重构版本。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

void roll_dice(char *combo)
{
    int dice1, dice2, dice3, dice4, dice5;
    int upper = 6;
    time_t t;

    /* Intializes random number generator - using this in lieu of sodium */
    srand((unsigned) time(&t));

    dice1 = rand() % upper + 1;
    dice2 = rand() % upper + 1;
    dice3 = rand() % upper + 1;
    dice4 = rand() % upper + 1;
    dice5 = rand() % upper + 1;

    sprintf(combo, "%d%d%d%d%d", dice1, dice2, dice3, dice4, dice5);
}

int get_word(char *combo, char *word)
{
    FILE *stream;
    char line[20];
    char *wordline;

    stream = fopen("eff_large_wordlist.txt", "r");  /* Placed a copy of the file into the executable file's folder for testing */

    if (NULL == stream)
    {
        printf("Failed to open wordlist\n");
        return -1;
    }

    while (fgets(line, sizeof(line), stream))
    {
        wordline = strstr(line, combo);

        if (wordline != NULL)   /* Copy over the data in local variable wordline to character array pointer "word", starting at the text after the five-digit number */
        {
            int j = 0;
            for (int i = 6; i < 20; i++)
            {
                if (wordline[i] == '\0')
                    break;
                if (wordline[i] >= ' ' && wordline[i] <= '~')
                {
                    word[j] = wordline[i];
                    j++;
                    word[j] = '\0';
                }
            }
            break;
        }
    }

    fclose(stream);

    return 0;   /* Needed this to address compiler warning */
}

int main(void)
{
    char combo[6];
    roll_dice(combo);
    printf("Dice numbers: %s\n", combo);

    char word1[20];

    if (get_word(combo, word1) < 0)
    {
        return 1;
    }

    printf("First word: %s\n", word1);

    // TODO: get 6 words

需要注意的几点:

  • 更正了函数调用中和函数内的指针引用,以便正确引用字符数组来解决编译器警告。
  • 只是因为我没有“钠”库,标准随机数生成被使用,因为这似乎是利用这个库/工具包的理由;最终结果仍然是正确的随机数。
  • “strcpy”函数可以像前面的评论中建议的那样使用;然而,由于意图是获取与五位随机数相关联的单词,因此使用“for”循环来解析从文件中获取的行。
  • 而且,为了保持测试的独立性,文本文件被放置在与可执行程序相同的文件夹中,只是为了保持简单。

有了所有这些,以下是一些测试在终端的输出数据。

craig@Vera:~/C_Programs/Console/Sodium/bin/Release$ ./Sodium 
Dice numbers: 66233
First word: waving
craig@Vera:~/C_Programs/Console/Sodium/bin/Release$ ./Sodium 
Dice numbers: 33253
First word: haggler
craig@Vera:~/C_Programs/Console/Sodium/bin/Release$ ./Sodium 
Dice numbers: 66251
First word: whinny
craig@Vera:~/C_Programs/Console/Sodium/bin/Release$ ./Sodium 
Dice numbers: 66251
First word: whinny

因此,要认识到指针引用如何与字符数组一起使用。你可能想做一些进一步的教程研究这个概念。

相关问题