printf char array segment fault C - c

So I have written a code that sorts words in the right order. The words are being stored via pointers and I have initialized another char array in the program to store the char* argv.
The last for loop is what prints segment fault and I can't figure out why.
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
int i, j;
char *key;
char a[argc-1];
for(i=1; i < argc; i++){
a[i-1]= tolower(argv[i]);
}
for (i = 2; i < argc; i++) {
key = argv[i];
j = i-1;
while (j >= 1 && strcmp(argv[j], key) > 0) {
argv[j+1] = argv[j];
j--;
}
argv[j+1] = key;
}
for(i = 1; i < argc; i++){
a[i-1] = *argv[i];
}
for (i = 1; i < argc ; i++){
puts(argv[i]);
}
for(i = 0; i < argc-1; i++){
printf("%s", a[i]);
}
return 0;
}
input
./a.out orange banana apple
output
apple
banana
orange
Segmentation fault

Your compiler should also give you a warnging on:
printf("%s", a[i]);
warning: format specifies type 'char *' but the argument has type
'char' [-Wformat]
If you change that to a %cit works fine.
As the warning says, when you use it %s printf is expecting a string or a char* and will treat it as such. a[i] is an integer type that can reference an invalid location in memory. So the correct way to do it would be to either use %c, and print a character; or use %s and pass a char* as the second argument.
Or perchance you want args in a. Change the delcaration.
char *a[argc-1];
Then change the assignment.
a[i-1] = argv[i];

There are multiple problems in your code
The line a[i - 1] = tolower(argv[i]) is wrong because tolower() takes int as paramater and you are passing char * so it's converting a pointer to int which is legal in c but does not guarantee defined behavior.
You are not setting the '\0' terminator on the a array which is another cause for problems, specifically when you try to use it as a string, a char array is not a string unless, it's a sequence of printable bytes with a terminating '\0' byte.
Allocating argc - 1 is not going to work because
The value of argc is the number of passed parameters to the executable.
If it was the length of the corresponding argv as found by strlen() you would need 1 more byte not less, so it would be argc + 1 in any case.

Why are you using %s
for(i = 0; i < argc-1; i++){
printf("%s", a[i]);
try %c

Related

I need to organize a string in an ascending form but im not sure what is the error

I'm trying to organize a string but my code fails. I'm not sure what I'm doing wrong, or even if my code is close to what I'm trying to do.
void ordenar(int lengh, char string[])
{
char aux[20];
for (int i = 0; i < lengh; i++) {
for (int j = 0; j < lengh; j++){
if (strcmp(string[j], string[i] > 0)) {
strcpy(aux, string[i]);
strcpy(string[i], string[j]);
strcpy(string[j], aux);
}
}
}
}
int main()
{
char nombre_prueba[10] = "Leandro";
ordenar(7, nombre_prueba);
for (int i = 0; i < 7; i++) {
printf("%s",nombre_prueba);
}
return 0;
}
I asked a friend to help, but he has the same problem.
Your compiler should be issuing some pretty serious diagnostic messages, that you should not be ignoring. If it is quiet, you need need to turn up your compiler warning level:
For gcc or clang, use -Wall -Wextra
For MSVC, use /Wall
The strcmp function takes two strings as arguments, as const char *. In your code, string[j] and string[i] are both of type char, an integral value representing a single character in the string.
Even if you had two strings to compare, the > 0 is within the argument list of the function call, which becomes much more clear with better formatting.
strcmp(string[j], string[i] > 0)
This function is not needed to reorder the contents of a string. With an ASCII encoding, you could compare each character directly.
Note that the printf specifier %s prints a single string. This call is correct, printf("%s", nombre_prueba);, but you are repeating it seven times.
With minimal changes, your code could look like:
#include <stdio.h>
void ordenar(int lengh, char string[])
{
for (int i = 0; i < lengh; i++) {
for (int j = 0; j < lengh; j++) {
if (string[j] > string[i]) {
char t = string[i];
string[i] = string[j];
string[j] = t;
}
}
}
}
int main(void)
{
char nombre_prueba[10] = "Leandro";
ordenar(7, nombre_prueba);
printf("%s\n", nombre_prueba);
}
which prints Ladenor. Note that uppercase letters come before lowercase letters in the ASCII table. This creates a string in ASCIIbetical order.

In c, when I use %s to print out the contents of a char array it prints blank, but when I loop over it and print each character it works

Just implementing a simple sorting algorithm to sort a string. I tried printing out the buff char array with printf("%s\n") but it came out blank. The contents of the array are there, though, and I checked with printing out each character of it. What am I missing here?
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
if (argc != 2)
{
printf("usage: ./sortstring string");
exit(1);
}
int size = 1; // 1 to account for '\0'
for (int i = 0; argv[1][i] != '\0'; i++)
{
size += 1;
}
char buff[size];
strcpy(buff, argv[1]);
char temp;
for (int i = 0; i < size; i++)
{
for (int j = i + 1; j < size; j++)
{
if (tolower(buff[i]) > tolower(buff[j]))
{
temp = buff[i];
buff[i] = buff[j];
buff[j] = temp;
}
}
}
// printf("%s\n", buff);
for (int i = 0; i < size; i++)
{
printf("%c", buff[i]);
}
return 0;
}
Change "%c" to "%d" in printf and see the result.
for (int i = 0; i < size; i++)
{
printf("%d", buff[i]);
}
strcpy copies terminating null byte with the source string.
You sorted terminating null byte with other characters.
Your sorting function is probably sorting the null character to position 0.
Instead of attempting to manually count characters in "argc[1]", you could just use the "strlen" function. So, instead of
int size = 1; // 1 to account for '\0'
for (int i = 0; argv[1][i] != '\0'; i++)
{
size += 1;
}
You could use
int size = strlen(argv[1]);
Regards.
The problem is that you're initializing size with 1. I know you did that because you need one more char to \0, but after that, either you need to loop through size - 1 or you can decrease the value of size before your for loops.
Another thing you can do is: initialize size with 0, and use size + 1 while creating your array.

Copying argv and checking it for a palindrome

I'm writing a program to check for palindromes. I recently picked up C and was wondering is there a reason why my take on it won't work? Does it have something to do with my use of directly copying argv into a char array
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc,char *argv[]){
int i;
int a;
int size;
for(a = 1; a < argc; a++){
char *reverseThis = argv[a];
char *normal = argv[a];
size = strlen(reverseThis);
for(i = 0; i < size; i++){
reverseThis[i] = normal[size - i - 1];
}
for(i = 0; i < size; i++){
reverseThis[i] = tolower(reverseThis[i]);
normal[i] = tolower(normal[i]);
}
if(strcmp(reverseThis,normal)==0){
printf("\"%s\": on palindromi\n",argv[i]);
}
else
printf("\"%s\": ei ole palindromi\n",argv[i]);
}
return 0;
}
In your code you are not copying the strings, you assigned both normal and reverseThis to same string argv[a].In reverseThis you need to copy argv[a] after allocating memory.
Just modify your code in forloop:
for(a = 1; a < argc; a++){
char *normal = argv[a];
size = strlen(normal);
char *reverseThis = (char*)malloc((size+1)*sizeof(char));
int j=0;
for(i = size-1; i >= 0; i++){
reverseThis[j++] = normal[i];
}
reverseThis[j]='\0';
.
.
You are using an incorrect approach.
For starters this loop
for(i = 0; i < size; i++){
reverseThis[i] = normal[size - i - 1];
}
copies the right half of the string in reverse order in the left half of the string totally overwriting its left part.
For example if you have a string like this "123456" then after the loop it will look like "654456"
This comparison also does not make sense
if(strcmp(reverseThis,normal)==0){
because the both pointers point to the same string. So the condition always yields true.
Take into account that these declarations
char *reverseThis = argv[a];
char *normal = argv[a];
do not copy the original string pointed to by argv[a]. The declared pointers just point to the first character of the same string.
And here is a typo
printf("\"%s\": on palindromi\n",argv[i]);
^^^
The task can be done simpler without changing the strings.
For example
size_t n = strlen( argv[a] );
size_t i = 0;
while ( i < n / 2 && tolower( ( unsigned char )argv[i] ) == tolower( ( unsigned char )argv[n -i - 1] ) ) ++i;
if ( i == n / 2 )
{
printf("\"%s\": on palindromi\n", argv[a]);
}
else
{
printf("\"%s\": ei ole palindromi\n",argv[a]);
}
If you indeed need to copy the strings then either declare variable length arrays (if the compiler supports them) or allocate arrays dynamically. For example (declaring variable length arrays):
size = strlen( argv[a] );
char reverseThis[size + 1];
char normal[size + 1];
strcpy( reverseThis, argv[a] );
strcpy( normal, argv[a] );
You don't need to reverse the string and compare to find out whether the input string is palindrome or not.
You can simply compare the characters of the string starting from both ends of the string and move one character forward from the start of the string and one character backward from the end of the string. If all character matches till you reach to mid of string then the string is palindrome otherwise not a palindrome.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc,char *argv[]){
int i, a, size;
for(a = 1; a < argc; a++){
char *ptr= argv[a];
int notpalindrom = 0;
size = strlen(ptr) - 1;
for(i = 0; i < size; ){
if (tolower(ptr[i++]) != tolower(ptr[size--])){
notpalindrom = 1;
break;
}
}
if (notpalindrom)
printf ("%s is not palindrom\n", ptr);
else
printf ("%s is palindrom\n", ptr);
}
return 0;
}
The first "if" take from argv in index "i", i == to the last change that has changed in the last "for", in your case i == 4, and the program crashed cause their isn't string in that member, to fix that you should change the "i" to 0 before the "if".

warning: assignment makes integer from pointer without a cast [enabled by default]

My code is:
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[]){
int i = 0;
int count = argc - 1;
char *numbers = malloc(count * sizeof(char));
for(i = 1 ; i <= argc ; i++){
printf("%s ", argv[i]);
numbers[i] = argv[i];
}
printf ("%s \n", numbers);
return 0;
}
The error that came is:
tamal#baba-desktop:~/Desktop/c$ cc experiment.c -o experiment
experiment.c: In function ‘main’:
experiment.c:10:16: warning: assignment makes integer from pointer
without a cast [enabled by default]
I tried numbers[i] = &(argv[i]); at line 10. Still the same result.
In your code, numbers[i] is of type char, whereas, argv[i] is of type char * and both are not the same. That's why the warning is there.
In the second case, numbers[i] = &(argv[i]); is also wrong, as &(argv[i]); is also not a char.
The bottom line is, you have a char pointer numbers and when you use indexing operator on it, you get the inidiviual elements as char, i.e., all the numbers[i] are of type char. You need to assign another char value to it.
You have to dereference argv twice to get the char value numbers[i] = *(*(argv + i));
This should be done as if there are more than 2 arguments:
int count = argc - 1;
char **numbers = (char **)malloc(count * sizeof(char*));
for(i = 0 ; i < argc ; i++){
printf("%s ", argv[i + 1]);
// Stores all arguments except "experiment"(i.e. exe file).
numbers[i] = argv[i + 1];
}
for(i = 0; i <= count; i++){
printf("%s\n", numbers[i]);
}
And if there is only two arguments then it should be done simply as:
char *number = (char *)malloc(1*sizeof(char));
// Store argument other than provided .exe file i.e. "experiment"
number = argv[1];
printf("%s\n", number);

Need String initialization advice

I got an assignment for wich i have to write an program that will take the letters in the first parameter string, and find them in the second parameter string like so:
./a.out "lolabab" "ablcocllcab"
the program needs to print "loab", because each letter should only be printed once.
here's the main part of my program
char *do_stuff(char *s1, char *s2)
{
int i, j, k;
char *out;
out = malloc(sizeof(char) * str_len(s1));
i = 0;
j = 0;
k = 0;
while (s2[j] != '\0' && s1[i] != '\0')
{
if (s2[j] == s1[i])
{
if (check_char(out, s1[i]) == 0)
{
out[k] = s1[i];
k++;
}
i++;
j = -1;
}
j++;
}
return (out);
}
my question is: if I dont initialize "out" i have a problem.
i initialize it with malloc at the moment, but i am not allowed to use malloc :).
any other way i tried, seems to not work for me (segmentation fault).
So how do i initialize a string without using malloc?
It's probably obvious, but i'm new at this so pls help. Thanks!
You can always pass the output buffer as a parameter
void do_stuff(char *s1, char *s2, char *out /* some large enough char [] */)
{
int i, j, k;
i = 0;
j = 0;
k = 0;
while (s2[j] != '\0' && s1[i] != '\0')
{
if (s2[j] == s1[i])
{
if (check_char(out, s1[i]) == 0)
{
out[k] = s1[i];
k++;
}
i++;
j = -1;
}
j++;
}
}
and in the calling function
char result[SOME_REASONABLE_SIZE] = {0} /* initialize it for the check_char function */;
do_stuff(argv[1], argv[2], result);
you should check that the function recieved the 2 arguments of course.
One more thing, try not to use strlen in the check char function, pass the current string length k to it, that way your program would be more efficient.
Use the fact that the number of characters is constant (and relatively small):
#include <limits.h>
#define CHAR_NUM (1<<CHAR_BIT)
#define FLAG(x) (1<<(x))
void get_common_chars(char* s1,char* s2,char out[CHAR_NUM])
{
int i,n;
int flags[CHAR_NUM] = {0};
for (i=0; s1[i]!=0; i++)
flags[(unsigned char)s1[i]] |= FLAG(1);
for (i=0; s2[i]!=0; i++)
flags[(unsigned char)s2[i]] |= FLAG(2);
n = 0;
for (i=0; i<CHAR_NUM; i++)
if (flags[i] == FLAG(1)|FLAG(2))
out[n++] = (char)i;
out[n] = 0;
}
If you're only interested in non-capital letters, then you can further improve it:
#define MIN_CHAR 'a'
#define MAX_CHAR 'z'
#define CHAR_NUM (MAX_CHAR-MIN_CHAR+1)
#define FLAG(x) (1<<(x))
void get_common_chars(char* s1,char* s2,char out[CHAR_NUM])
{
int i,n;
int flags[CHAR_NUM] = {0};
for (i=0; s1[i]!=0; i++)
if (MIN_CHAR <= s1[i] && s1[i] <= MAX_CHAR)
flags[s1[i]-MIN_CHAR] |= FLAG(1);
for (i=0; s2[i]!=0; i++)
if (MIN_CHAR <= s2[i] && s2[i] <= MAX_CHAR)
flags[s2[i]-MIN_CHAR] |= FLAG(1);
n = 0;
for (i=0; i<CHAR_NUM; i++)
if (flags[i] == FLAG(1)|FLAG(2))
out[n++] = (char)(MIN_CHAR+i);
out[n] = 0;
}
Here is a usage example:
#include <stdio.h>
int main(int argc,char* argv[])
{
char common_chars[CHAR_NUM];
if (argc >= 3)
{
get_common_chars(argv[1],argv[2],common_chars);
printf("%s\n",common_chars);
}
return 0;
}
If I understand correctly what you need, you should not create a new string, but use the command-line parameters, which are available in the arguments of main().
When you write
int main(int argc, char** argv) {
The compiler will arrange so that argc is the number of command-line arguments, and argv is an array of strings with the arguments. The first, argv[0], is the program name, and the rest are arguments passed to the program.
So this is one way to get your assignment done (high-level description only -- the rest is yours!)
Take the first argument, argv[1], and loop over it, character by character. For each character, try to find it in the other argument, argv[2]. If you find it, print the single character.
No need to allocate memory at all!
edit: if you don't want to print doubles, then one way would be to keep a static array that you could use as an index of already printed characters:
static int printed[26] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
When you print c, set its position to 1. And only print if the character's position is zero.
It's up to you to find out how to find the index of an arbitrary character (and to decide wether you want to differentiate between upper and lower case).

Resources