I am trying to make a program that will encrypt and decrypt when user enters the string they want to encrypt/decrypt for argv[2] and enters either "encrypt" or "decrypt" for argv[3]. Here is the code I am trying to compile and run as of now
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
int i;
// char *string;
char *key_ch;
char key_int;
char *string_ = calloc(80, 1);
string = argv[1];
char encrypted_string[strlen(string)];
char decrypted_string[strlen(string)];
//char *key_ch;
//char key_int;
string = argv[1];
key_ch = argv[2];
key_int = atoi(key_ch);
if (argc < 3)
{
printf("Not enough arguments!\n");
exit (1);
}
if (strcmp(argv[3], "encrypt") == 0)
{
i = 0;
while(i <= strlen(string)-1)
{
encrypted_string[i] = string[i] + key_int;
i++;
}
// printf("Encrypted string: ");
i = 0;
while (i <= strlen(string) -1)
{
printf("%c", encrypted_string[i]);
i++;
}
printf("\n");
}
if (strcmp(argv[3], "decrypt") == 0)
{
i = 0;
while(i <= strlen(string) -1)
{
decrypted_string[i] = string[i] - key_int;
i++;
}
// printf("Decrypted String: ");
i = 0;
while (i <= strlen(string) -1)
{
printf("%c", decrypted_string[i]);
i++;
}
printf("\n");
}
return 0;
}
When I try to compile it without the -Wall command it compiles fine but when I run the program I am getting a segmentation fault, when I compile with -Wall I am getting
sam0.c:9:24: warning: 'string' is used uninitialized in this function [-Wuninitialized]
char encrypted_string[strlen(string)];
Can anyone possible shine some light on this error? Thank you
Edit:
Changed my code to your suggestions. I am not getting a compiling error at all even when using "-Wall" however somewhere in my program it is causing me to get a segmentation fault... any ideas? I put quotes around where I changed my code for reference in case I did it wrong.
warning: 'string' is used uninitialized in this function
[-Wuninitialized]
char *string; creates a pointer to char. At this point it is not yet a string, but you are using it as a string argument.
Before using char *string; it must have memory assigned, and should be initialized. Among other methoods, this can be done by:
char *string = calloc(80, 1);//initializes with known values (NULL).
Now string is usable, but has zero length. Values can be assigned via string functions:
strcpy(string, argv[1]);
sprintf(string, "%s", argv[1]);
strcat(string, argv[1]);
... more string functions
When using input from command line, argv, argc, malloc/calloc and string cpy functions can be avoided by using strdup. a value can be assigned like this:
if(argc == 2)
{
char *string = strdup(argv[1]);
if(!string) return -1;
...
EDIT (addressing your OP edit)
You are now using two different variables: string_ and string
char *string_ = calloc(80, 1);
^
string = argv[1];
Make them the same throughout your code and it should build and run.
When a meaningful value for the variable can be determined, try to declare and initialize the variable in one step:
char *string = argv[1];
char encrypted_string[strlen(string)];
char decrypted_string[strlen(string)];
char *key_ch = argv[2];
char key_int = atoi(key_ch);
This will fix the warning: you were trying to get the length of an uninitialized string.
(here assuming C99. char encrypted_string[strlen(string)] is a VLA. If you're restricted to C89 only Rykker's answer works).
Also:
use const to prevent unwanted modifications, e.g.
const char *string = argv[1];
/* ... */
const char *key_ch = argv[2]
string is a constant so you can get its length just one time (do not recalculate the length every time... take a look at Shlemiel the painter's algorithm). So
const char *string = argv[1];
const int length = strlen(string);
char encrypted_string[length];
char decrypted_string[length];
const char *key_ch = argv[2];
char key_int = atoi(key_ch);
check inputs: what happen if
argc < 4
argv[3] isn't in ("encrypt", "decrypt")
key_ch is not a number
key_int is too big
you're printing encrypted_string / decryped_string one character at a time... and it works, but consider that if you want to manipulate them as 'strings' they aren't null terminated and their length isn't correct.
don't repeat yourself. The differences between the encryption and the decryption phases are minimal: you can use just one buffer and change key_int to a negative value to decrypt.
char *string;
char encrypted_string[strlen(string)];
char decrypted_string[strlen(string)];
This results in undefined behavior - you try to get the length of an uninitialized piece of memory (I'm amazed that your program doesn't crash). You may only call strlen after assigning something to string.
The easiest fix (assuming you don't want to start using malloc, strdup, etc.) would be moving the last two lines after string = argv[1];
Also, you need to check argc to make sure enough arguments have been passed!
Related
I am currently facing a problem while coding. The problem is I want to loop through a string and compare each index with another string's index. And at the same time, copy the character to the other string if it does not have it yet. The code below is where I got stuck:
I compiled this and got the error: comparison between pointer and integer ('char' and 'string' (aka 'char *')) [-Werror,-Wpointer-integer-compare]
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[1])
{
string key = argv[1], key2[26];
for (int i = 0; key[i] != '\0' ; i++)
{
int j = 0;
if (key[i] != key2[j]) // I got an error here
{
key2[j] = key[i];
j++
}
}
printf("%s\n", key2);
}
An array of 26 char is needed, not 26 string
// string key = argv[1], key2[26];
string key = argv[1];
char key2[26];
key2[] not initialized
Unclear how OP wants to populate this. Perhaps from next argument?
Best to test argc first
if (argc < 2) {
fprintf(stderr, "Missing argument\n");
return EXIT_FAILURE;
}
string key = argv[1];
...
Drop unneeded 1
// int main(int argc, string argv[1])
int main(int argc, string argv[])
This shows why you shouldn't hide a pointer behind a typedef and why hiding how C works from novice programmers doesn't benefit anyone.
There is no string type in C. string here is just a typedef for a char *. The following statement:
string key = argv[1], key2[26];
allocates memory for a char *, and an array[26] of char *, which are not synonymous.
Or in other words:
It declares:
char *key = argv[1];
char *key2[26];
Note that the these statements only allocated memory for the pointers, not what they point to. In this case, you did initialize key, but forgot to initialize key2.
The statement:
if (key[i] != key2[j])
is then invalid. Because key[i] evaluates to a char, where key2[j] evaluates to a char * (that you didn't allocate memory for, nor initialize).
Fix:
Simply declare key2 to be a char *, not an array of char *s.
Now find the length of argv[1] before copying (it could result in an overflow elsewise).
size_t len = strlen (argv[1]);
Now allocate memory for key2:
char *key2 = malloc (len + 1); /* One for the '\0' byte */
if (!key2) {
/* ENOMEM, handle error accordingly */
}
Now copy the strings with strcpy() and continue on with the loop. And free() the memory when you're done with it.
Minor: You are accessing argv[1] before checking if its valid.
if (argc != 2) {
fprintf (stderr, "/* Print usage message here */.\n");
return EXIT_FAILURE;
}
I am very new to C, and I have created a function that removes special characters from a string and returns a new string (without the special characters).
At first glance, this seemed to be working well, I now need to run this function on the lines of a (huge) text file (1 Million sentences). After a few thousand lines/sentences (About 4,000) I get a seg fault.
I don't have much experience with memory allocation and strings in C, I have tried to figure out what the problem with my code is, unfortunately without any luck.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
char *preproccessString(char *str) {
// Create a new string of the size of the input string, so this might be bigger than needed but should never be too small
char *result = malloc(sizeof(str));
// Array of allowed chars with a 0 on the end to know when the end of the array is reached, I don't know if there is a more elegant way to do this
// Changed from array to string for sake of simplicity
char *allowedCharsArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Initalize two integers
// i will be increased for every char in the string
int i = 0;
// j will be increased every time a new char is added to the result
int j = 0;
// Loop over the input string
while (str[i] != '\0') {
// l will be increased for every char in the allowed chars array
int l = 0;
// Loop over the chars in the allowed chars array
while (allowedCharsArray[l] != '\0') {
// If the char (From the input string) currently under consideration (index i) is present in the allowed chars array
if (allowedCharsArray[l] == toupper(str[i])) {
// Set char at index j of result string to uppercase version of char currently under consideration
result[j] = toupper(str[i]);
j++;
}
l++;
}
i++;
}
return result;
}
Here is the rest of the program, I think the problem is probably here.
int main(int argc, char *argv[]) {
char const * const fileName = argv[1];
FILE *file = fopen(fileName, "r");
char line[256];
while (fgets(line, sizeof(line), file)) {
printf("%s\n", preproccessString(line));
}
fclose(file);
return 0;
}
You have several problems.
You're not allocating enough space. sizeof(str) is the size of a pointer, not the length of the string. You need to use
char *result = malloc(strlen(str) + 1);
+ 1 is for the terminating null byte.
You didn't add a terminating null byte to the result string. Add
result[j] = '\0';
before return result;
Once you find that the character matches an allowed character, there's no need to keep looping through the rest of the allowed characters. Add break after j++.
Your main() function is never freeing the results of preprocessString(), so you might be running out of memory.
while (fgets(line, sizeof(line), file)) {
char *processed = preproccessString(line);
printf("%s\n", processed);
free(processed);
}
You could address most of these problems if you have the caller pass in the result string, instead of allocating it in the function. Just use two char[256] arrays in the main() function.
int main(int argc, char *argv[])
{
char const* const fileName = argv[1];
FILE* file = fopen(fileName, "r");
char line[256], processed[256];
while (fgets(line, sizeof(line), file)) {
processString(line, processed);
printf("%s\n", processed);
}
fclose(file);
return 0;
}
Then just change the function so that the parameters are:
void preprocessString(const char *str, char *result)
A good rule of thumb is to make sure there is one free for every malloc/calloc call.
Also, a good tool to keep note of for the future is Valgrind. It's very good at catching these kinds of errors.
There are some major issues in your code:
the amount of memory allocated is incorrect, sizeof(str) is the number of bytes in a pointer, not the length of the string it points to, which would also be incorrect. You should write char *result = malloc(strlen(str) + 1);
the memory allocated in preproccessString is never freed, causing memory leaks and potentially for the program to run out of memory on very large files.
you do not set a null terminator at the end of the result string
Lesser issues:
you do not check if filename was passed nor if fopen() succeeded.
there is a typo in preproccessString, it should be preprocessString
you could avoid memory allocation by passing a properly sized destination array.
you could use isalpha instead of testing every letter
you should cast the char values as unsigned char when passing them to toupper because char may be a signed type and toupper is undefined for negative values except EOF.
there are too many comments in your source file, most of which are obvious but make the code less readable.
Here is a modified version:
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
// transform the string in `str` into buffer dest, keeping only letters and uppercasing them.
char *preprocessString(char *dest, const char *str) {
int i, j;
for (i = j = 0; str[i] != '\0'; i++) {
if (isalpha((unsigned char)str[i])
dest[j++] = toupper((unsigned char)str[i]);
}
dest[j] = '\0';
return dest;
}
int main(int argc, char *argv[]) {
char line[256];
char dest[256];
char *filename;
FILE *file;
if (argc < 2) {
fprintf(stderr, "missing filename argument\n");
return 1;
}
filename = argv[1];
if ((file = fopen(filename, "r")) == NULL) {
fprintf(stderr, "cannot open %s: %s\n", filename, strerror(errno));
return 1;
}
while (fgets(line, sizeof(line), file)) {
printf("%s\n", preprocessString(dest, line));
}
fclose(file);
return 0;
}
The following proposed code:
cleanly compiles
performs the desired functionality
properly checks for errors
properly checks for length of input string parameter
makes use of characteristic of strchr() also checking the terminating NUL byte
limits scope of visibility of local variables
the calling function is expected to properly cleaning up by passing the returned value to free()
the calling function is expected to check the returned value for NULL
informs compiler the user knows and accepts when an implicit conversion is made.
moves allowedCharsArray to 'file static scope' so does not have to be re-initialized on each pass through the loop and marks as 'const' to help the compiler catch errors
and now the proposed code: (note: edited per comments)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
char *preproccessString(char *str)
{
// Create a new string of the size of the input string, so this might be bigger than needed but should never be too small
char *result = calloc( sizeof( char ), strlen(str)+1);
if( !result )
{
perror( "calloc failed" );
return NULL;
}
// Array of allowed chars
static const char *allowedCharsArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Loop over the input string
for( int j=0, i=0; str[i]; i++)
{
if( strchr( allowedCharsArray, (char)toupper( str[i] ) ) )
{
// Set char at index j of result string to uppercase version of char currently under consideration
result[j] = (char)toupper(str[i]);
j++;
}
}
return result;
}
I think the problem is you are using malloc which allocates memory from the heap and since you are calling this function again and again you are running out of memory.
To solve this issue you have to call the free() function on the pointer returned by your preprocessString function
In your main block
char *result=preprocessString(inputstring);
//Do whatever you want to do with this result
free(result);
I am writing a program that takes a list of path ( environmental variable), splits the paths and prints it. When compiling it I get a segfault. The following is my output on GDB :
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400eb0 in dest (name=0x7fffffffbce0 "PATH") at executables.c:100
100 dest[i] = malloc(srclen+1);
On valgrind:
==21574== 1 errors in context 2 of 3:
==21574== Use of uninitialised value of size 8
==21574== at 0x400EB0: dest (executables.c:100)
==21574== by 0x400B5B: main (main.c:9)
This is my function:
char** dest(char *name){
int i=0;
char *vp;
const char s[2]=":";
char *token;
char **dest;
name[strlen(name)-1]='\0';
vp=getenv(name);
if(vp == NULL){
exit(1);
}
token =strtok(vp,s);
while( token != NULL ){
size_t srclen = strlen(token);
dest[i] = malloc(srclen+1);
strcpy(dest[i], token);
token = strtok(NULL, s);
i++;
}
dest[i]=NULL;
return dest;
}
And this is my main:
#include "executables.h"
int main(int argc, char **argv){
char *path;
char name[BUFSIZ];
printf("enter name of environment variable:\n");
fgets(name,BUFSIZ,stdin);
char **p=dest(name);
int j=0;
while(p[j]!=NULL){
printf("%s\n",p[j]);
j++;
}
return(0);
}
Use strdup(). Saves steps (accounts for
'\0' too). You have to allocate some memory before hand for the approach you're using. Otherwise you might want a linked list and allocate packets instead of using the array pattern. When you say dest[i] = <ptr value> you're indexing to an offset of unallocated memory and storing something there, so it's a segvio.
#include <string.h>
#define MAXTOKENS 10000
char **get_dest(char *name) {
// Since dest has to be exposed/persist beyond this function all
// need dynamically allocate (malloc()) rather than stack allocate
// of the form of: char *dest[MAXTOKENS].
char *dest = malloc(MAXTOKENS * sizeof (char *)); // <--- need to allocate storage for the pointers
char *vp;
if ((vp = getenv(name)) == NULL)
exit(-1); // -1 is err exit on UNIX, 0 is success
int i = 0;
char *token = strtok(vp, ":");
while (token != NULL) {
dest[i] = strdup(token); // <=== strdup()
token = strtok(NULL, ":");
i++;
}
// dest[i] = NULL; // Why are you setting this to NULL after adding token?
return dest;
}
It's better if main() takes care of passing a proper null-terminated string to the get_dest() function because main is where the finicky fgets() is handled. Generally you want to do things locally where it makes the most sense and is most relevant. If you ever took your get_dest() function and used it somewhere where the strings were not read by fgets() it would just be a wasted step to overwrite the terminator there. So by initializing the char array to zeroes before fgets() you don't have to worry about setting the trailing byte to '\0'.
And finally probably not good to have your function name dest the same name as the variable it returns dest. In some situations having multiple symbols in your program with the same name can get you into trouble.
#include "executables.h"
int main(int argc, char **argv) {
char *path;
char name[BUFSIZ] = { 0 }; // You could initialize it to zero this way
printf("enter name of environment variable:\n");
// bzero(name, BUFSIZ); //... or you could initialize it to zero this way then
fgets(name, BUFSIZ, stdin);
char **p = get_dest(name);
int j = 0;
while(p[j] != NULL) {
printf("%s\n", p[j]);
j++;
free(p[j]); // like malloc(), strdup'd() strings must be free'd when done
}
free(p);
return 0;
}
dest[i] = malloc(srclen + 1);
You need to allocate memory for the pointer to char pointers (dest) as well as each char pointer stored in dest. In the code you provided, neither step is taken.
From the manpage of getenv:
Notes
...
As typically implemented, getenv() returns a pointer to a string
within the environment list. The caller must take care not to modify
this string, since that would change the environment of the process.
Your code violates that rule:
vp=getenv(name);
...
token =strtok(vp,s);
This is an illegal memory write operation.
Here's the code, which is supposed to execute the first command in history when "history 1" is entered:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char *argv[])
{
int i=0; int j=0; int k=0;
char inputString[100];
char *result=NULL;
char delims[] = " ";
char historyArray[100][100] = {0};
char *tokenArray[100][100] ;
do
{
j = 0;
printf("hshell>");
gets(inputString);
strcpy (historyArray[k], inputString);
k++;
// Break the string into parts
result = strtok(inputString, delims);
while (result!=NULL)
{
//result2 = result;
strcpy(tokenArray[j], result);
//puts(result);
j++;
result= strtok(NULL, delims);
//puts(tokenArray[j]);
}
//j = 0;
puts(tokenArray[0]);
puts(tokenArray[1]);
if (strcmp(tokenArray[0], "exit") == 0)
{
return 0;
}
else if (strcmp(tokenArray[0], "history") == 0)
{
if (j>1)
{
strcpy (result,historyArray[atoi(tokenArray[j-1])]);
}
else
{
//print history array
for (i=0; i<k;i++)
printf("%i. %s\n", i+1, historyArray[i]);
}
}
else
{
printf("Command not found\n");
}
}while (1);
}
However, it crashes. When in debugging, I noticed two things: - the array (tokenArray) address is out of bounds and - Access Violation (Segmentation Fault). You can see the errors in the images below.
What am I missing? What am I doing wrong?
The reason why you are dealing with segmentation fault is because you are trying to copy a string into the memory that has not yet been allocated. You have defined result as a char* and just assigned NULL to it, so trying to copy string into it is wrong:
char *result = NULL;
// ...
strcpy(result, historyArray[atoi(tokenArray[j-1])]);
You need to allocate some memory, that result will point to. Then strcpy can be used to copy string into this memory. You can either use malloc to allocate it dynamically or you can define result as an temporary variable with automatic storage duration (i.e. char result[100];).
Also note that
char *tokenArray[100][100];
defines a two-dimensional array of pointers to char. But what you actually need in this case is an array of strings, so you need to get rid of * just like #cnicutar has pointed out.
And one more note:
strcpy(result,historyArray[atoi(tokenArray[j-1])]);
is quite dangerous thing to do, because when atoi fails, you are trying to access the element out of array bounds, which produces undefined behavior, thus I recommend you doing something like this:
char tokenArray[100][100] = {0};
int index;
char indexString[100] = "8";
if (sscanf(indexString, "%d", &index) == 1) // integer successfully retrieved
{
strcpy(tokenArray[index], "some string");
printf("%s", tokenArray[8]);
}
You probably meant char tokenArray[100][100]; which creates 100 tokens with 100 characters each in 1 token.
writing char *tokenArray[100][100] literally means tokenArray is an array of 100 arrays, which contain 100 char *. But each of those char * points to a random addresses if it is not assigned a proper address.
You are getting a segmentation violation error because one of the char * contains an address which you cannot access.
What I am trying to do is to break the user input in parts with whitespace as a delimiter, copy the parts into the array (tokenAr) and compare the tokenAr[0] (the first part) if it is equal to sHistory. if they are equal, check the value of tokenAr[1] if it is "1", "2" etc, to execute the corresponding command that is entered in the history array. This is what i have tried to far and it crashes. I am using TCC on Windows x64.
EDIT: I forgot to mention that I began learning C, just two days ago.
EDIT2: I run the program in a debugger and it has raised an Acces Violation(Segmentation Fault) in line if(strcmp(tokenArPtr[0],sHistory)==0)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
int i=1; int j=1; int k=0;
char history[100][100] = {0};
char sKey[] = "exit";
char sInput[100];
char sHistory[]="history";
do
{
//gather user input
printf ("hshell> ");
fgets (sInput, 100, stdin);
strcpy(history[i],sInput);
i++;
//END_gather user input
//Tokenizing
char delims[] = " ";
char *tokenArPtr[5];
char *result = NULL;
result = strtok(sInput, delims);
tokenArPtr[0] = result;
while (result!=NULL)
{
puts(result);
result= strtok(NULL, delims);
tokenArPtr[k+1] = result;
puts(tokenArPtr[k]);
puts("=====");
k++;
}
k=0;
/*
//END_Tokenizing
if(strcmp(tokenArPtr[0],sHistory)==0)
{
for(j=1;j<i;j++)
{
printf("%d. %s \n",j,history[j]);
}
}
else if (strcmp (sKey,tokenArPtr[0]) != 0)
{
printf("\nCommand not found \n");
}*/
}while (strcmp (sKey,sInput) != 0);
return 0;
}
EDIT 3: I used the result variable instead of the tokenArPtr directly, but when debugging, I noticed that the values of the array are not being updated.
Which type does strtok return? char *. What is the type of tokenAr[k]? char. What type does strcmp expect as input? char * and char *. What is the type of tokenAr[0]? char.
See a problem? You should. The * is pretty significant.
Assuming tokenAr is declared like char *tokenAr[2];, how many char * values can tokenAr store? What happens when k exceeds 2? You need to ensure you don't overflow your tokenAr array.
history is uninitialised. Using an uninitialised variable is undefined behaviour. I suggest initialising it, like this: char history[100][100] = { 0 };
Which book are you reading?
While tokenizing, the loop will never end because the test is on the variable "result" that will never change... So you're finally going to a buffer overflow with "tokenAr"... Modify your code to test "tokenAr".
Edit: And tokenAR should be an array... (I don't know how it can compile...)
There are many problems... First of all you should include string.h which will show you some errors in compilation.
I believe that the main problem is here:
char tokenAr[2];
result = strtok(sInput, delims);
while (result!=NULL)
{
tokenAr[k] = strtok(NULL, delims);
k++;
}
tokenAr should be an array of pointers, not chars. And are you sure that k will never exceed 2? An assertion would help debugging.