How to make strcmp() work in my C program - c

I am about to make an encryption of a textstring, using another textstring as a "key" for my encryption. It is basically just a reorganization of the ASCII-characters.
The key is given and is structured in a bad way, requiring some extra programming. The ASCII-characters are listed in this key with one charachter for each line, and the corresponding encrypted characters two indexes away.
This is the example of "key.txt":
A g
B 9
C ü
D (
E z
...continuing for all ASCII-characters. An encryption in my program would therfore result in:
"EDABEDA" -> "z(g9z(g"
When I am doing the encryption.
I let the program take in an input-string and I create another string with the encryption-key-characters.
I go through each character in the input string with a for-sling. For each character in the input string, I check if there is a matching character in my encryption-key string. In the encryption key I jump 4 steps at a time since I am going to encrypt and only have to compare A,B,C,D...
I use strcmp() to find a match. And when there is a match, when the character in the input string is the same as in the encryption key, I write the encrypted character to the output string. The encrypted character is placed two indexes ahead of the main character in the key-string.
The warnings occur in the strcmp()
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 3000
void encryption(char *input);
int main()
{
char input[] = "EDABEDA";
encryption(input);
}
void encryption(char *input)
{
int length,i,k;
size_t result;
char dicc[1000];
char output[SIZE];
FILE *f;
f = fopen("key.txt","r");
fseek(f, 0, SEEK_END);
length = ftell(f);
fseek(f, 0, SEEK_SET);
result = fread(dicc, 1, length, f);
int lenDic = strlen(dicc);
int lenInp = strlen(input);
for (i = 0 ; i < lenInp ; i++)
{
for (k = 4 ; k < lenDic ; k = k + 4)
{
if (strcmp(input[i],dicc[k]) == 0)
{
output[i] = dicc[k+2];
printf("%c",output[i]);
}
}
}
fclose(f);
}
I get the below warnings, and the program doesn't work. Anyone who can help me with this strcmp-warning and know how I should rearrange my program in order to meet its requirements?
warning: passing argument 1 of ‘strcmp’ makes pointer from integer without a cast [enabled by default]
if (strcmp(input[i],dicc[k]) == 0)
^
In file included from crypt.c:3:0:
/usr/include/string.h:144:12: note: expected ‘const char *’ but argument is of type ‘char’
extern int strcmp (const char *__s1, const char *__s2)
^
warning: passing argument 2 of ‘strcmp’ makes pointer from integer without a cast [enabled by default]
if (strcmp(input[i],dicc[k]) == 0)
^
In file included from crypt.c:3:0:
/usr/include/string.h:144:12: note: expected ‘const char *’ but argument is of type ‘char’
extern int strcmp (const char *__s1, const char *__s2)
^

Use strcmp() to compare strings, not characters. For your case this
if (input[i] == dicc[k])
should work.
Note that char is just an integer type not a string type, there is no string type in c. And one more thing, write safe code! Check of fopen() succeeded, and if length doesn't exceed 999. Pretty much every thing that can be considered an error.

Related

error: incompatible integer to pointer conversion assigning to 'string' (aka 'char *') from 'int' C

This is incomplete code but I'm trying to make copies of argv[1] that are uppercase and lowercase, but get an error message. If it's an inefficient way to go about it in general or completely wrong I would appreciate any tips, but is there a way to fix the error in this specific case?
I'm extremely new to coding as you can tell by the question, so I'm sorry if it's a stupid one, but where might the error be occurring? I realize somehow argv[1] is being converted into an integer but I neither know where nor how to really fix it.
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
string keyu[26];
string keyl[26];
string key = argv[1];
for (int u = 0; u < strlen(key); u++)
{
keyu[u] = toupper(key[u]);
}
for (int l = 0; l < strlen(key); l++)
{
keyl[l] = tolower(key[l]);
}
The error it gives out is:
14:17: error: incompatible integer to pointer conversion assigning to 'string' (aka 'char *') from 'int' [-Werror,-Wint-conversion]
keyu[u] = toupper(key[u]);
These declarations
string keyu[26];
string keyl[26];
are equivalent to
char * keyu[26];
char * keyl[26];
That is they are arrays of pointers.
This in these statements
keyu[u] = toupper(key[u]);
keyl[l] = tolower(key[l]);
you are trying to assign an integer (as for example toupper(key[u])) to a pointer (as for example keyu[u])
It seems you want actually to declare arrays of characters
char keyu[26];
char keyl[26];
Pay attention to that you should append the arrays with the terminating zero character '\0' after the for loops to guarantee that the arrays contain strings.

C function to return greatest ascii value

I'm trying to create a C function to return a pointer to the char with the greatest ascii value in a string, but my function is returning 'H' instead of 'o'. I think it's something to do with the if statement in the for loop but I'm not sure what the problem is. Any help is appreciated. Thanks!
char * select_max(char str[]);
int main(void) {
printf("%c\n",select_max("Hello"));
printf("All tests passed successfully.\n");
}
char *select_max(char str[]){
int length = strlen(str);
if(length<1){//returns 0 if string length is less than one
printf("Invalid string.\n");
return 0;
}
char *max = str;
for(int i=0;i<length;i++){
if(str[i] > max){
max = str[i];
}
}
return *max;
}
Try adding a printout so that you can see your bug in action. For example, just before your if:
printf("%c > %c = %d\n", str[i], max, (str[i] > max));
I believe this will quickly reveal the bug. [Hint: there are 5 bugs in your program.]
Compiler is nice enough to give you quite useful warnings, you should follow them, read carefully and try to understand. It won't point out your logical errors but you can deduce these too in this case.
main.c: In function 'main':
main.c:4:5: warning: implicit declaration of function 'printf' [-Wimplicit-function-declaration]
printf("%c\n",select_max("Hello"));
^~~~~~
main.c:4:5: warning: incompatible implicit declaration of built-in function 'printf'
main.c:4:5: note: include '<stdio.h>' or provide a declaration of 'printf'
main.c: In function 'select_max':
main.c:10:18: warning: implicit declaration of function 'strlen' [-Wimplicit-function-declaration]
int length = strlen(str);
^~~~~~
main.c:10:18: warning: incompatible implicit declaration of built-in function 'strlen'
main.c:10:18: note: include '<string.h>' or provide a declaration of 'strlen'
You first need to include the libraries that you use functions from, printf is from stdio.h and strlen is from string.h.
main.c:22:12: warning: returning 'char' from a function with return type 'char *' makes pointer from integer without a cast [-Wint-conversion]
return *max;
^~~~
You appear to be confusing chars with char*s. Since select_max is logically supposed to return just a character, (and you are already returning a char) declaring it as below will suffice.
#include<stdio.h>
#include<string.h>
char select_max( char str[] );
main.c:19:17: warning: assignment to 'char *' from 'char' makes pointer from integer without a cast [-Wint-conversion]
max = str[i];
And in the implementation of select_max, there is a similar problem. The temporary variable to hold the highest character is of type char* where it just needs to be a char:
char select_max(char str[]){
// ^___
int length = strlen(str);
if(length<1){//returns 0 if string length is less than one
printf("Invalid string.\n");
return 0;
}
// Initialize it to 0, has to be lower than any ASCII letters in order your algorithm to work. DO NOT leave it uninitialized.
char max = 0; // <---
for(int i=0;i<length;i++){
if(str[i] > max){
max = str[i];
}
}
return max;
}
The code above defines max as a char and initializes it to 0. Assigning str to it was pointless anyway. You are iterating through your string one character at a time and storing the character in a temporary container.
Note that the code in the for loop had already written as if it was of type char.
The same algorithm of course can be implemented in various ways but I suppose this was what you were trying to do.

Add numbers to filename

I want to store data in different files. Therefore I want to create files as follows: data_1.log, data_2.log, ..., data_N.log. The appendix .log is not necessary but would be nice. All my approaches failed so far. Here is one sample that is probably close to what I need:
#include <stdio.h>
#include <string.h>
char get_file_name(int k){
int i, j;
char s1[100] = "logs/data_";
char s2[100];
snprintf(s2, 100, "%d", k);
for(i = 0; s1[i] != '\0'; ++i);
for(j = 0; s2[j] != '\0'; ++j, ++i){
s1[i] = s2[j];
}
s1[i] = '\0';
return s1;
}
int main(){
char file_name[100];
for(int k=0; k<10; k++){
// Get data
// ...
// Create filename
strcpy(file_name, get_file_name(k));
printf("%s", file_name);
// fp = fopen(file_name, "w+");
// Write data to file
// print_results_to_file();
// fclose(fp);
}
return 0;
}
At the moment I get the following errors which I don't understand:
string.c: In function ‘get_file_name’:
string.c:14:12: warning: returning ‘char *’ from a function with return type ‘char’ makes integer from pointer without a cast [-Wint-conversion]
return s1;
^~
string.c:14:12: warning: function returns address of local variable [-Wreturn-local-addr]
string.c: In function ‘main’:
string.c:24:27: warning: passing argument 2 of ‘strcpy’ makes pointer from integer without a cast [-Wint-conversion]
strcpy(file_name, get_file_name(k));
^~~~~~~~~~~~~~~~
In file included from string.c:2:
/usr/include/string.h:121:14: note: expected ‘const char * restrict’ but argument is of type ‘char’
extern char *strcpy (char *__restrict __dest, const char *__restrict __src)
^~~~~~
Is there a more simpler way to create such filenames? I can't believe that there isn't one.
There are various issues with this code and rather than correcting them one by one here’s an alternative approach. It’s not the only one but it’s simple and should be easy to understand and adapt:
#include <stdio.h>
void get_file_name(int k, char* buffer, size_t buflen) {
snprintf(buffer, buflen, "logs/data_%d.log", k);
}
int main() {
const size_t BUFLEN = 50;
char file_name[BUFLEN];
for (int i = 0; i < 10; i++) {
get_file_name(i, file_name, BUFLEN);
printf("%s\n", file_name);
// Code for writing to file.
}
}
A few details:
Rather than attempting to return (pointers to) memory, this function passes a buffer that is written to. It’s up to the caller to ensure that the buffer is big enough (this is always the case here, but if the actual filenames are longer, you should add logic that inspects the return value of snprintf and performs appropriate error handling).
The actual logic of the function requires only a single call to snprintf, which already performs everything you require, so it’s unclear whether having a separate function is even necessary or helpful.
The above uses variable-length arrays. If you want to ensure constant buffers, you can use a #define instead of a const size_t variable for the buffer length. However, using a variable-length array here is fine, and some compilers even convert it into a constant array.
As mentioned in comments, it’s important that you (a) read and understand the documentation of the functions you’re using, and (b) read and understand the compiler error messages.
The function get_file_name has return type char
char get_file_name(int k){
but it returns an object of type char *
char s1[100] = "logs/data_";
//...
return s1;
Moreover the returned pointer points to a local array s1 that will not alive after exiting the function.
In this call
strcpy(file_name, get_file_name(k));
the type of the second argument (that is char according to the function get_file_name declaration) shall be char *.
There is neither the function print_results_to_file declaration nor its definition.
According to the C Standard the function main without parameters shall be declared like
int main( void )
I would write the function get_file_name the following way
#include <stdio.h>
#include <string.h>
char * get_file_name( char *file_name, size_t n, size_t padding )
{
const char *common_part = "logs/data_";
snprintf( file_name, n, "%s%zu", common_part, padding );
return file_name;
}
int main( void )
{
enum { N = 100 };
char file_name[N];
for ( size_t i = 0; i < 10; i++ ) puts( get_file_name( file_name, N, i ) );
}
The program output is
logs/data_0
logs/data_1
logs/data_2
logs/data_3
logs/data_4
logs/data_5
logs/data_6
logs/data_7
logs/data_8
logs/data_9
There are several problems with your code, but the biggest one is that you are trying to return a pointer to a local variable from get_file_name.
This is a big no no since the memory allocated for char s1[100] in get_file_name is freed immediately after return.
The rest of the errors are because you forgot the * in char get_file_name(int k).
There are several possible solutions:
Pass in a char array for the function to fill.
Use a global variable (This is considered a bad practice).
Dynamically allocate the memory.
Make the local variable static (this is a bit hacky, but legal)
Your errors are easily explained:
get_file_name should return a char but you create a char[] and return this(it isthe same as char*)
get_file_name returns the adress of an array that is created in the function itself. After the function ends, the array may be overwritten. Add the array as parameter or use malloc
strcpy does not work because it expects a char* (char[]) and not a char. get_file_name returns a char.
print_results_to_file is not defined. You may need to include other files you use in the program (e.g. if the function is implemented in a file func.c the prototype should be in a file called func.h that is included via #include "func.h".

Error : format'%s' expects argument of type 'char *', but argument 2 has type 'int' [-Wformat=]

I am currently trying to do my own shell, and it has to be polyglot.
So I tryed to implement a function that reads the lines in a .txt file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// globals
char lang[16] = {'t','r','y'};
char aMsg[512];
// functions
void takeFile() {
int i =0;
char namFil[32];
char lg[16];
FILE * file;
char tmp[255];
char * line = tmp;
size_t len = 0;
ssize_t read;
strcpy(namFil,"/media/sf_Projet_C/");
strcpy(lg,lang);
strcat(lg, ".txt");
strcat(namFil, lg);
file = fopen(namFil, "r");
printf("%s\n", namFil);
while((read = getline(&line,&len, file)) != -1) {
aMsg[i] = *line;
i++;
}
}
enum nMsg {HI, QUIT};
int main(void) {
takeFile();
printf("%s\n%s\n", aMsg[HI], aMsg[QUIT]);
}
I am on win7 but I compile with gcc on a VM.
I have a warning saying :
format'%s' expects argument of type 'char *', but argument 2 (and 3) has type 'int' [-Wformat=]
I tried to execute the prog with %d instead of %s and it prints numbers.
I don't understand what converts my aMsg into a int.
My try.txt file is just :
Hi
Quit
The contents of your text file have nothing to do with the warning, which is generated by the compiler before your program ever runs. It is complaining about this statement:
printf("%s\n%s\n", aMsg[HI], aMsg[QUIT]);
Global variable aMsg is an array of char, so aMsg[HI] designates a single char. In this context its value is promoted to int before being passed to printf(). The %s field descriptor expects an argument of type char *, however, and GCC is smart enough to recognize that what you are passing is incompatible.
Perhaps you had in mind
printf("%s\n%s\n", &aMsg[HI], &aMsg[QUIT]);
or the even the equivalent
printf("%s\n%s\n", aMsg + HI, aMsg + QUIT);
but though those are valid, I suspect they won't produce the result you actually want. In particular, given the input data you specified and the rest of your program, I would expect the output to be
HQ
Q
If you wanted to read in and echo back the whole contents of the input file then you need an altogether different approach to both reading in and writing out the data.
Let's take a closer look on the problematic line:
printf("%s\n%s\n", aMsg[HI], aMsg[QUIT]);
The string you would like to print expects 2 string parameters. You have aMsg[HI] and aMsg[QUIT]. These two are pointing to a char, so the result is one character for each. All char variables can be interpreted as a character or as a number - the character's ID number. So I assume the compiler resolves these as int types, thus providing you that error message.
As one solution you merely use %c instead of %s.
However, I suspect you want to achieve something else.
I'm completely guessing, but I think what you want is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// globals
char lang[16] = {'t','r','y'};
char *aMsg[512];
// functions
void takeFile() {
int i =0;
char namFil[32];
char lg[16];
FILE * file;
char tmp[255];
char * line = tmp;
size_t len = 0;
ssize_t read;
strcpy(namFil,"/media/sf_Projet_C/");
strcpy(lg,lang);
strcat(lg, ".txt");
strcat(namFil, lg);
file = fopen(namFil, "r");
printf("%s\n", namFil);
while((read = getline(&line,&len, file)) != -1) {
aMsg[i] = malloc(strlen(line)+1);
strcpy(aMsg[i], line);
i++;
}
fclose(file);
}
enum nMsg {HI, QUIT};
int main(void) {
takeFile();
printf("%s\n%s\n", aMsg[HI], aMsg[QUIT]);
free(aMsg[HI]);
free(aMsg[QUIT]);
return 0;
}

Pointer difference and size_t

I want to allocate memory for holding a field extracted from a given string. The size of the field is determined by the difference of two pointers, see the following minimal example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(int argc, char *argv[])
{
const char line[] = "foo,bar,baz";
char *field_start = line;
char *field_end;
char *field;
field_end = strchr(line, ',');
field = malloc(field_end - field_start + 1);
memcpy(field, field_start, field_end - field_start);
*(field + (field_end - field_start)) = '\0';
printf("field=\"%s\"\n", field);
/* ... */
return (0);
}
Compiling this code with clang -Weverything -o ex ex.c results in the following warnings:
ex.c:14:41: warning: implicit conversion changes signedness: 'long' to 'unsigned long'
[-Wsign-conversion]
field = malloc(field_end - field_start + 1);
~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~^~~
ex.c:15:39: warning: implicit conversion changes signedness: 'long' to 'unsigned long'
[-Wsign-conversion]
memcpy(field, field_start, field_end - field_start);
~~~~~~ ~~~~~~~~~~^~~~~~~~~~~~~
As I understand it, the result of the pointer difference is of ptrdiff_t type while the malloc/memcpy expect an argument of type size_t.
So my question is how to address this and to eliminate the warning? As
field_end >= field_start the difference cannot become negative, so could the
above be safely casted to size_t
field = malloc(size_t(field_end - field_start + 1));
memcpy(field, size_t(field_start, field_end - field_start));
or are the any problems I'm overlooking?
Note:
There are no checks for return values in the above just for simplicity. field_start and _end should be const of course.
field_end >= field_start only holds in case strchr does not return NULL, i.e. nothing in the type system tells the compiler that this indeed always holds. Hence the warning is warranted. However, if you make sure that this is not the case, then (size_t)(field_end - field_start) should be fine. In order to not duplicate this all over, I'd add
size_t field_len;
/* memchr & null-check go here */
field_len = (size_t)(field_end - field_start);
...and then use field_len all over.
That being said, you may want to replace your malloc/memcpy combination with a call to strndup.

Resources